From 35c0431cec184c9b5b0ab557caa9e0026ffc08a8 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Tue, 19 Sep 2017 13:45:51 -0400 Subject: [PATCH] Update Java to match C's internal changes. --- core/c/mpw-types.c | 7 +- core/java/algorithm/build.gradle | 1 + core/java/algorithm/pom.xml | 5 + .../com/lyndir/masterpassword/MPConstant.java | 56 +- .../lyndir/masterpassword/MPKeyPurpose.java | 90 + .../{MPSiteType.java => MPResultType.java} | 114 +- ...eTypeClass.java => MPResultTypeClass.java} | 4 +- .../lyndir/masterpassword/MPSiteVariant.java | 103 - .../com/lyndir/masterpassword/MasterKey.java | 81 +- .../lyndir/masterpassword/MasterKeyV0.java | 199 +- .../lyndir/masterpassword/MasterKeyV1.java | 51 +- .../lyndir/masterpassword/MasterKeyV2.java | 68 +- .../lyndir/masterpassword/MasterKeyV3.java | 33 +- .../lyndir/masterpassword/model/MPSite.java | 28 +- .../model/MPSiteMarshaller.java | 4 +- .../model/MPSiteUnmarshaller.java | 24 +- .../lyndir/masterpassword/model/MPUser.java | 12 +- .../lyndir/masterpassword/MPTestSuite.java | 17 +- .../com/lyndir/masterpassword/MPTests.java | 46 +- .../tests/src/main/resources/mpw_tests.xml | 74 +- .../lyndir/masterpassword/MasterKeyTest.java | 10 +- gradle/pom.xml | 1 - gradle/settings.gradle | 3 - .../masterpassword/EmergencyActivity.java | 38 +- .../lyndir/masterpassword/Preferences.java | 14 +- .../main/res/layout/activity_emergency.xml | 8 +- .../src/main/res/values/strings.xml | 2 +- platform-darwin/Resources/Data/ciphers.plist | 90 - platform-independent/cli-java/build.gradle | 14 - platform-independent/cli-java/pom.xml | 98 - .../java/com/lyndir/masterpassword/CLI.java | 189 -- .../lyndir/masterpassword/package-info.java | 28 - .../cli-java/src/main/resources/logback.xml | 15 - .../cli-java/src/main/scripts/bashlib | 1958 ----------------- .../cli-java/src/main/scripts/install | 56 - .../cli-java/src/main/scripts/mpw | 9 - .../cli-java/src/main/scripts/mpw.bashrc | 15 - .../gui/model/IncognitoSite.java | 16 +- .../masterpassword/gui/model/ModelSite.java | 12 +- .../masterpassword/gui/model/ModelUser.java | 2 +- .../lyndir/masterpassword/gui/model/Site.java | 6 +- .../gui/view/PasswordFrame.java | 22 +- 42 files changed, 589 insertions(+), 3034 deletions(-) create mode 100644 core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java rename core/java/algorithm/src/main/java/com/lyndir/masterpassword/{MPSiteType.java => MPResultType.java} (63%) rename core/java/algorithm/src/main/java/com/lyndir/masterpassword/{MPSiteTypeClass.java => MPResultTypeClass.java} (94%) delete mode 100644 core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java delete mode 100644 platform-darwin/Resources/Data/ciphers.plist delete mode 100644 platform-independent/cli-java/build.gradle delete mode 100644 platform-independent/cli-java/pom.xml delete mode 100644 platform-independent/cli-java/src/main/java/com/lyndir/masterpassword/CLI.java delete mode 100644 platform-independent/cli-java/src/main/java/com/lyndir/masterpassword/package-info.java delete mode 100644 platform-independent/cli-java/src/main/resources/logback.xml delete mode 100755 platform-independent/cli-java/src/main/scripts/bashlib delete mode 100755 platform-independent/cli-java/src/main/scripts/install delete mode 100755 platform-independent/cli-java/src/main/scripts/mpw delete mode 100644 platform-independent/cli-java/src/main/scripts/mpw.bashrc diff --git a/core/c/mpw-types.c b/core/c/mpw-types.c index 60fb1ca0..ad74b057 100644 --- a/core/c/mpw-types.c +++ b/core/c/mpw-types.c @@ -50,14 +50,11 @@ const MPResultType mpw_typeWithName(const char *typeName) { return MPResultTypeDeriveKey; } - // Lower-case and trim optionally leading "Generated" string from typeName to standardize it. - size_t stdTypeNameOffset = 0; + // Lower-case typeName to standardize it. size_t stdTypeNameSize = strlen( typeName ); - if (strstr( typeName, "Generated" ) == typeName) - stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" )); char stdTypeName[stdTypeNameSize + 1]; for (size_t c = 0; c < stdTypeNameSize; ++c) - stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] ); + stdTypeName[c] = (char)tolower( typeName[c] ); stdTypeName[stdTypeNameSize] = '\0'; // Find what password type is represented by the type name. diff --git a/core/java/algorithm/build.gradle b/core/java/algorithm/build.gradle index c37d3236..d39c8889 100644 --- a/core/java/algorithm/build.gradle +++ b/core/java/algorithm/build.gradle @@ -8,6 +8,7 @@ dependencies { compile (group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.6-p10') { exclude( module: 'joda-time' ) } + compile group: 'com.lyndir.lhunath.opal', name: 'opal-crypto', version: '1.6-p10' compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0' compile group: 'org.jetbrains', name: 'annotations', version: '13.0' diff --git a/core/java/algorithm/pom.xml b/core/java/algorithm/pom.xml index 757fa0d3..3da84269 100644 --- a/core/java/algorithm/pom.xml +++ b/core/java/algorithm/pom.xml @@ -31,6 +31,11 @@ + + com.lyndir.lhunath.opal + opal-crypto + 1.6-p9 + diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java index 15cbefbd..1610f496 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPConstant.java @@ -32,68 +32,16 @@ public final class MPConstant { /* Environment */ - /** - * mpw: default user name if one is not provided. - */ - public static final String env_userName = "MP_USERNAME"; - /** - * mpw: default site type if one is not provided. - * - * @see MPSiteType#forOption(String) - */ - public static final String env_siteType = "MP_SITETYPE"; - /** - * mpw: default site counter value if one is not provided. - */ - public static final String env_siteCounter = "MP_SITECOUNTER"; /** * mpw: default path to look for run configuration files if the platform default is not desired. */ - public static final String env_rcDir = "MP_RCDIR"; + public static final String env_rcDir = "MPW_RCDIR"; /** * mpw: permit automatic update checks. */ - public static final String env_checkUpdates = "MP_CHECKUPDATES"; + public static final String env_checkUpdates = "MPW_CHECKUPDATES"; /* Algorithm */ - /** - * scrypt: CPU cost parameter. - */ - public static final int scrypt_N = 32768; - /** - * scrypt: Memory cost parameter. - */ - public static final int scrypt_r = 8; - /** - * scrypt: Parallelization parameter. - */ - public static final int scrypt_p = 2; - /** - * mpw: Master key size (byte). - */ - public static final int mpw_dkLen = 64; - /** - * mpw: Input character encoding. - */ - public static final Charset mpw_charset = Charsets.UTF_8; - /** - * mpw: Platform-agnostic byte order. - */ - public static final ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN; - /** - * mpw: Site digest. - */ - public static final MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256; - /** - * mpw: Key ID hash. - */ - public static final MessageDigests mpw_hash = MessageDigests.SHA256; - /** - * mpw: validity for the time-based rolling counter. - */ - public static final int mpw_counter_timeout = 5 * 60 /* s */; - - public static final int MS_PER_S = 1000; } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java new file mode 100644 index 00000000..db01353c --- /dev/null +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java @@ -0,0 +1,90 @@ +//============================================================================== +// This file is part of Master Password. +// Copyright (c) 2011-2017, Maarten Billemont. +// +// Master Password is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Master Password is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You can find a copy of the GNU General Public License in the +// LICENSE file. Alternatively, see . +//============================================================================== + +package com.lyndir.masterpassword; + +import com.google.common.collect.ImmutableList; +import com.lyndir.lhunath.opal.system.logging.Logger; +import java.util.List; +import java.util.Locale; +import javax.annotation.Nullable; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NonNls; + + +/** + * @author lhunath, 14-12-02 + */ +public enum MPKeyPurpose { + Password( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ), + Login( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ), + Answer( "recovery", "Generate an account recovery token.", "com.lyndir.masterpassword.answer" ); + + static final Logger logger = Logger.get( MPResultType.class ); + + private final String shortName; + private final String description; + private final String scope; + + MPKeyPurpose(final String shortName, final String description, @NonNls final String scope) { + this.shortName = shortName; + this.description = description; + this.scope = scope; + } + + public String getShortName() { + return shortName; + } + + public String getDescription() { + return description; + } + + public String getScope() { + return scope; + } + + /** + * @param shortNamePrefix The name for the purpose to look up. It is a case insensitive prefix of the purpose's short name. + * + * @return The purpose registered with the given name. + */ + @Nullable + @Contract("!null -> !null") + public static MPKeyPurpose forName(@Nullable final String shortNamePrefix) { + + if (shortNamePrefix == null) + return null; + + for (final MPKeyPurpose type : values()) + if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) )) + return type; + + throw logger.bug( "No purpose for name: %s", shortNamePrefix ); + } + + public static MPKeyPurpose forInt(final int keyPurpose) { + + return values()[keyPurpose]; + } + + public int toInt() { + + return ordinal(); + } +} diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultType.java similarity index 63% rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultType.java index eb90dcf7..4725d90a 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultType.java @@ -24,7 +24,6 @@ import com.lyndir.lhunath.opal.system.logging.Logger; import java.util.*; import javax.annotation.Nullable; import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NonNls; /** @@ -32,15 +31,13 @@ import org.jetbrains.annotations.NonNls; * * @author lhunath */ -public enum MPSiteType { +public enum MPResultType { - GeneratedMaximum( "Max", "20 characters, contains symbols.", // - ImmutableList.of( "x", "max", "maximum" ), // NON-NLS + GeneratedMaximum( "Maximum", "20 characters, contains symbols.", // ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), // - MPSiteTypeClass.Generated, 0x0 ), + MPResultTypeClass.Generated, 0x0 ), GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", // - ImmutableList.of( "l", "long" ), // NON-NLS ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ), new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ), new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ), @@ -52,65 +49,55 @@ public enum MPSiteType { new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ), new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ), new MPTemplate( "CvccCvcvCvccno" ) ), // - MPSiteTypeClass.Generated, 0x1 ), + MPResultTypeClass.Generated, 0x1 ), GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", // - ImmutableList.of( "m", "med", "medium" ), // NON-NLS ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), // - MPSiteTypeClass.Generated, 0x2 ), + MPResultTypeClass.Generated, 0x2 ), GeneratedBasic( "Basic", "8 characters, no symbols.", // - ImmutableList.of( "b", "basic" ), // NON-NLS ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), // - MPSiteTypeClass.Generated, 0x3 ), + MPResultTypeClass.Generated, 0x3 ), GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", // - ImmutableList.of( "s", "short" ), // NON-NLS ImmutableList.of( new MPTemplate( "Cvcn" ) ), // - MPSiteTypeClass.Generated, 0x4 ), + MPResultTypeClass.Generated, 0x4 ), GeneratedPIN( "PIN", "4 numbers.", // - ImmutableList.of( "i", "pin" ), // NON-NLS ImmutableList.of( new MPTemplate( "nnnn" ) ), // - MPSiteTypeClass.Generated, 0x5 ), + MPResultTypeClass.Generated, 0x5 ), GeneratedName( "Name", "9 letter name.", // - ImmutableList.of( "n", "name" ), // NON-NLS ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), // - MPSiteTypeClass.Generated, 0xE ), + MPResultTypeClass.Generated, 0xE ), GeneratedPhrase( "Phrase", "20 character sentence.", // - ImmutableList.of( "p", "phrase" ), // NON-NLS ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ), new MPTemplate( "cv cvccv cvc cvcvccv" ) ), // - MPSiteTypeClass.Generated, 0xF ), + MPResultTypeClass.Generated, 0xF ), StoredPersonal( "Personal", "AES-encrypted, exportable.", // - ImmutableList.of( "personal" ), // NON-NLS ImmutableList.of(), // - MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ), + MPResultTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ), StoredDevicePrivate( "Device", "AES-encrypted, not exported.", // - ImmutableList.of( "device" ), // NON-NLS ImmutableList.of(), // - MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate ); + MPResultTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate ); - static final Logger logger = Logger.get( MPSiteType.class ); + static final Logger logger = Logger.get( MPResultType.class ); private final String shortName; private final String description; - private final List options; private final List templates; - private final MPSiteTypeClass typeClass; + private final MPResultTypeClass typeClass; private final int typeIndex; private final Set typeFeatures; - MPSiteType(final String shortName, final String description, final List options, final List templates, - final MPSiteTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) { + MPResultType(final String shortName, final String description, final List templates, + final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) { this.shortName = shortName; this.description = description; - this.options = options; this.templates = templates; this.typeClass = typeClass; this.typeIndex = typeIndex; @@ -131,11 +118,7 @@ public enum MPSiteType { return description; } - public List getOptions() { - return options; - } - - public MPSiteTypeClass getTypeClass() { + public MPResultTypeClass getTypeClass() { return typeClass; } @@ -154,35 +137,22 @@ public enum MPSiteType { } /** - * @param option The option to select a type with. It is matched case insensitively. - * - * @return The type registered for the given option. - */ - public static MPSiteType forOption(final String option) { - - for (final MPSiteType type : values()) - if (type.getOptions().contains( option.toLowerCase( Locale.ROOT ) )) - return type; - - throw logger.bug( "No type for option: %s", option ); - } - - /** - * @param name The name fromInt the type to look up. It is matched case insensitively. + * @param shortNamePrefix The name for the type to look up. It is a case insensitive prefix of the type's short name. * * @return The type registered with the given name. */ + @Nullable @Contract("!null -> !null") - public static MPSiteType forName(@Nullable final String name) { + public static MPResultType forName(@Nullable final String shortNamePrefix) { - if (name == null) + if (shortNamePrefix == null) return null; - for (final MPSiteType type : values()) - if (type.name().equalsIgnoreCase( name )) + for (final MPResultType type : values()) + if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) )) return type; - throw logger.bug( "No type for name: %s", name ); + throw logger.bug( "No type for name: %s", shortNamePrefix ); } /** @@ -190,10 +160,10 @@ public enum MPSiteType { * * @return All types that support the given class. */ - public static ImmutableList forClass(final MPSiteTypeClass typeClass) { + public static ImmutableList forClass(final MPResultTypeClass typeClass) { - ImmutableList.Builder types = ImmutableList.builder(); - for (final MPSiteType type : values()) + ImmutableList.Builder types = ImmutableList.builder(); + for (final MPResultType type : values()) if (type.getTypeClass() == typeClass) types.add( type ); @@ -205,11 +175,11 @@ public enum MPSiteType { * * @return The type registered with the given type. */ - public static MPSiteType forType(final int type) { + public static MPResultType forType(final int type) { - for (final MPSiteType siteType : values()) - if (siteType.getType() == type) - return siteType; + for (final MPResultType resultType : values()) + if (resultType.getType() == type) + return resultType; throw logger.bug( "No type: %s", type ); } @@ -219,17 +189,27 @@ public enum MPSiteType { * * @return All types that support the given mask. */ - public static ImmutableList forMask(final int mask) { + public static ImmutableList forMask(final int mask) { - int typeMask = mask & ~0xF; - ImmutableList.Builder types = ImmutableList.builder(); - for (final MPSiteType siteType : values()) - if (((siteType.getType() & ~0xF) & typeMask) != 0) - types.add( siteType ); + int typeMask = mask & ~0xF; + ImmutableList.Builder types = ImmutableList.builder(); + for (final MPResultType resultType : values()) + if (((resultType.getType() & ~0xF) & typeMask) != 0) + types.add( resultType ); return types.build(); } + public static MPResultType forInt(final int resultType) { + + return values()[resultType]; + } + + public int toInt() { + + return ordinal(); + } + public MPTemplate getTemplateAtRollingIndex(final int templateIndex) { return templates.get( templateIndex % templates.size() ); } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteTypeClass.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultTypeClass.java similarity index 94% rename from core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteTypeClass.java rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultTypeClass.java index e4ab3748..aed71f05 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteTypeClass.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultTypeClass.java @@ -23,13 +23,13 @@ package com.lyndir.masterpassword; * * @author lhunath */ -public enum MPSiteTypeClass { +public enum MPResultTypeClass { Generated( 1 << 4 ), Stored( 1 << 5 ); private final int mask; - MPSiteTypeClass(final int mask) { + MPResultTypeClass(final int mask) { this.mask = mask; } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java deleted file mode 100644 index 3c6256ba..00000000 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java +++ /dev/null @@ -1,103 +0,0 @@ -//============================================================================== -// This file is part of Master Password. -// Copyright (c) 2011-2017, Maarten Billemont. -// -// Master Password is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Master Password is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You can find a copy of the GNU General Public License in the -// LICENSE file. Alternatively, see . -//============================================================================== - -package com.lyndir.masterpassword; - -import com.google.common.collect.ImmutableList; -import com.lyndir.lhunath.opal.system.logging.Logger; -import java.util.List; -import java.util.Locale; -import javax.annotation.Nullable; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NonNls; - - -/** - * @author lhunath, 14-12-02 - */ -public enum MPSiteVariant { - Password( "Generate a key for authentication.", "Doesn't currently use a context.", // - ImmutableList.of( "p", "password" ), "com.lyndir.masterpassword" ), // NON-NLS - Login( "Generate a name for identification.", "Doesn't currently use a context.", // - ImmutableList.of( "l", "login" ), "com.lyndir.masterpassword.login" ), // NON-NLS - Answer( "Generate an answer to a security question.", "Empty for a universal site answer or\nthe most significant word(s) of the question.", // - ImmutableList.of( "a", "answer" ), "com.lyndir.masterpassword.answer" ); // NON-NLS - - static final Logger logger = Logger.get( MPSiteType.class ); - - private final String description; - private final String contextDescription; - private final List options; - private final String scope; - - MPSiteVariant(final String description, final String contextDescription, final List options, @NonNls final String scope) { - this.contextDescription = contextDescription; - - this.options = options; - this.description = description; - this.scope = scope; - } - - public String getDescription() { - return description; - } - - public String getContextDescription() { - return contextDescription; - } - - public List getOptions() { - return options; - } - - public String getScope() { - return scope; - } - - /** - * @param option The option to select a variant with. It is matched case insensitively. - * - * @return The variant registered for the given option. - */ - public static MPSiteVariant forOption(final String option) { - - for (final MPSiteVariant variant : values()) - if (variant.getOptions().contains( option.toLowerCase( Locale.ROOT ) )) - return variant; - - throw logger.bug( "No variant for option: %s", option ); - } - /** - * @param name The name fromInt the variant to look up. It is matched case insensitively. - * - * @return The variant registered with the given name. - */ - @Contract("!null -> !null") - public static MPSiteVariant forName(@Nullable final String name) { - - if (name == null) - return null; - - for (final MPSiteVariant type : values()) - if (type.name().equalsIgnoreCase( name )) - return type; - - throw logger.bug( "No variant for name: %s", name ); - } - -} diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java index d8135da7..af9775b3 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java @@ -86,16 +86,61 @@ public abstract class MasterKey { allowNativeByDefault = allowNative; } - protected MasterKey(@Nonnull final String fullName) { + protected MasterKey(final String fullName) { + Preconditions.checkArgument( !fullName.isEmpty() ); this.fullName = fullName; logger.trc( "fullName: %s", fullName ); } + /** + * Derive the master key for a user based on their name and master password. + * + * @param masterPassword The user's master password. + */ @Nullable @SuppressWarnings("MethodCanBeVariableArityMethod") protected abstract byte[] deriveKey(char[] masterPassword); + /** + * Derive the site key for a user's site from the given master key and site parameters. + * + * @param siteName A site identifier. + * @param siteCounter The result identifier. + * @param keyPurpose The intended purpose for this site key. + * @param keyContext A site-scoped key modifier. + */ + protected abstract byte[] siteKey(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose, + @Nullable String keyContext); + + /** + * Generate a site result token. + * + * @param siteName A site identifier. + * @param siteCounter The result identifier. + * @param keyPurpose The intended purpose for this site result. + * @param keyContext A site-scoped result modifier. + * @param resultType The type of result to generate. + * @param resultParam A parameter for the resultType. For stateful result types, the output of + * {@link #siteState(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}. + */ + public abstract String siteResult(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose, + @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam); + + /** + * Encrypt a stateful site token for persistence. + * + * @param siteName A site identifier. + * @param siteCounter The result identifier. + * @param keyPurpose The intended purpose for the site token. + * @param keyContext A site-scoped key modifier. + * @param resultType The type of result token to encrypt. + * @param resultParam The result token desired from + * {@link #siteResult(String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}. + */ + public abstract String siteState(String siteName, UnsignedInteger siteCounter, MPKeyPurpose keyPurpose, + @Nullable String keyContext, MPResultType resultType, @Nullable String resultParam); + public abstract Version getAlgorithmVersion(); @Nonnull @@ -125,9 +170,6 @@ public abstract class MasterKey { return idForBytes( getKey() ); } - public abstract String encode(@Nonnull String siteName, MPSiteType siteType, @Nonnull UnsignedInteger siteCounter, - MPSiteVariant siteVariant, @Nullable String siteContext); - public boolean isValid() { return masterKey != null; } @@ -150,17 +192,17 @@ public abstract class MasterKey { masterKey = deriveKey( masterPassword ); if (masterKey == null) - logger.dbg( "masterKey calculation failed after %.2fs.", (double)(System.currentTimeMillis() - start) / MPConstant.MS_PER_S ); + logger.dbg( "masterKey calculation failed after %.2fs.", (double) (System.currentTimeMillis() - start) / MPConstant.MS_PER_S ); else logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ), - (double)(System.currentTimeMillis() - start) / MPConstant.MS_PER_S ); + (double) (System.currentTimeMillis() - start) / MPConstant.MS_PER_S ); return this; } protected abstract byte[] bytesForInt(int number); - protected abstract byte[] bytesForInt(@Nonnull UnsignedInteger number); + protected abstract byte[] bytesForInt(UnsignedInteger number); protected abstract byte[] idForBytes(byte[] bytes); @@ -168,19 +210,19 @@ public abstract class MasterKey { /** * bugs: * - does math with chars whose signedness was platform-dependent. - * - miscounted the byte-length fromInt multi-byte site names. - * - miscounted the byte-length fromInt multi-byte full names. + * - miscounted the byte-length for multi-byte site names. + * - miscounted the byte-length for multi-byte full names. */ V0, /** * bugs: - * - miscounted the byte-length fromInt multi-byte site names. - * - miscounted the byte-length fromInt multi-byte full names. + * - miscounted the byte-length for multi-byte site names. + * - miscounted the byte-length for multi-byte full names. */ V1, /** * bugs: - * - miscounted the byte-length fromInt multi-byte full names. + * - miscounted the byte-length for multi-byte full names. */ V2, /** @@ -200,20 +242,5 @@ public abstract class MasterKey { return ordinal(); } - - public String toBundleVersion() { - switch (this) { - case V0: - return "1.0"; - case V1: - return "2.0"; - case V2: - return "2.1"; - case V3: - return "2.2"; - } - - throw new UnsupportedOperationException( strf( "Unsupported version: %s", this ) ); - } } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java index 18b08891..e3dce3ab 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java @@ -18,30 +18,68 @@ package com.lyndir.masterpassword; -import com.google.common.base.Preconditions; +import com.google.common.base.*; import com.google.common.primitives.Bytes; import com.google.common.primitives.UnsignedInteger; import com.lambdaworks.crypto.SCrypt; +import com.lyndir.lhunath.opal.crypto.CryptUtils; import com.lyndir.lhunath.opal.system.*; import com.lyndir.lhunath.opal.system.logging.Logger; import java.nio.*; +import java.nio.charset.Charset; import java.security.GeneralSecurityException; import java.util.Arrays; -import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.crypto.IllegalBlockSizeException; /** * bugs: - * - V2: miscounted the byte-length fromInt multi-byte full names. - * - V1: miscounted the byte-length fromInt multi-byte site names. + * - V2: miscounted the byte-length for multi-byte full names. + * - V1: miscounted the byte-length for multi-byte site names. * - V0: does math with chars whose signedness was platform-dependent. * * @author lhunath, 2014-08-30 */ public class MasterKeyV0 extends MasterKey { - private static final int MP_intLen = 32; + /** + * mpw: validity for the time-based rolling counter. + */ + protected static final int mpw_otp_window = 5 * 60 /* s */; + /** + * mpw: Key ID hash. + */ + protected static final MessageDigests mpw_hash = MessageDigests.SHA256; + /** + * mpw: Site digest. + */ + protected static final MessageAuthenticationDigests mpw_digest = MessageAuthenticationDigests.HmacSHA256; + /** + * mpw: Platform-agnostic byte order. + */ + protected static final ByteOrder mpw_byteOrder = ByteOrder.BIG_ENDIAN; + /** + * mpw: Input character encoding. + */ + protected static final Charset mpw_charset = Charsets.UTF_8; + /** + * mpw: Master key size (byte). + */ + protected static final int mpw_dkLen = 64; + /** + * scrypt: Parallelization parameter. + */ + protected static final int scrypt_p = 2; + /** + * scrypt: Memory cost parameter. + */ + protected static final int scrypt_r = 8; + /** + * scrypt: CPU cost parameter. + */ + protected static final int scrypt_N = 32768; + private static final int MP_intLen = 32; @SuppressWarnings("UnusedDeclaration") private static final Logger logger = Logger.get( MasterKeyV0.class ); @@ -59,112 +97,171 @@ public class MasterKeyV0 extends MasterKey { @Nullable @Override protected byte[] deriveKey(final char[] masterPassword) { + Preconditions.checkArgument( masterPassword.length > 0 ); + String fullName = getFullName(); - byte[] fullNameBytes = fullName.getBytes( MPConstant.mpw_charset ); + byte[] fullNameBytes = fullName.getBytes( mpw_charset ); byte[] fullNameLengthBytes = bytesForInt( fullName.length() ); + ByteBuffer mpBytesBuf = mpw_charset.encode( CharBuffer.wrap( masterPassword ) ); - String mpKeyScope = MPSiteVariant.Password.getScope(); - byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes ); - logger.trc( "key scope: %s", mpKeyScope ); - logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) ); + logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() ); + logger.trc( "fullName: %s", fullName ); + logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) ); - ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) ); + String keyScope = MPKeyPurpose.Password.getScope(); + logger.trc( "keyScope: %s", keyScope ); + + // Calculate the master key salt. + logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s", + keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName ); + byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( mpw_charset ), fullNameLengthBytes, fullNameBytes ); + logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) ); + + // Calculate the master key. + logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )", + scrypt_N, scrypt_r, scrypt_p ); byte[] mpBytes = new byte[mpBytesBuf.remaining()]; mpBytesBuf.get( mpBytes, 0, mpBytes.length ); Arrays.fill( mpBytesBuf.array(), (byte) 0 ); + byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()? + Arrays.fill( masterKeySalt, (byte) 0 ); + Arrays.fill( mpBytes, (byte) 0 ); + logger.trc( " => masterKey.id: %s", (masterKey == null)? null: (Object) idForBytes( masterKey ) ); - return scrypt( masterKeySalt, mpBytes ); + return masterKey; } @Nullable protected byte[] scrypt(final byte[] masterKeySalt, final byte[] mpBytes) { try { if (isAllowNative()) - return SCrypt.scrypt( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen ); + return SCrypt.scrypt( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen ); else - return SCrypt.scryptJ( mpBytes, masterKeySalt, MPConstant.scrypt_N, MPConstant.scrypt_r, MPConstant.scrypt_p, MPConstant.mpw_dkLen ); + return SCrypt.scryptJ( mpBytes, masterKeySalt, scrypt_N, scrypt_r, scrypt_p, mpw_dkLen ); } catch (final GeneralSecurityException e) { logger.bug( e ); return null; } - finally { - Arrays.fill( mpBytes, (byte) 0 ); - } } @Override - public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter, - final MPSiteVariant siteVariant, @Nullable final String siteContext) { - Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated ); + protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, + @Nullable final String keyContext) { Preconditions.checkArgument( !siteName.isEmpty() ); + logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() ); logger.trc( "siteName: %s", siteName ); - logger.trc( "siteCounter: %d", siteCounter.longValue() ); - logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant ); - logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType ); + logger.trc( "siteCounter: %d", siteCounter ); + logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() ); + logger.trc( "keyContext: %s", keyContext ); + String keyScope = keyPurpose.getScope(); + logger.trc( "keyScope: %s", keyScope ); + + // OTP counter value. if (siteCounter.longValue() == 0) - siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout ); + siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (mpw_otp_window * 1000)) * mpw_otp_window ); - String siteScope = siteVariant.getScope(); - byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset ); + // Calculate the site seed. + byte[] siteNameBytes = siteName.getBytes( mpw_charset ); byte[] siteNameLengthBytes = bytesForInt( siteName.length() ); byte[] siteCounterBytes = bytesForInt( siteCounter ); - byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset ); - byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length ); - logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "": siteContext ); - logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), - siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), - (siteContextBytes == null)? "(null)": siteContext ); + byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( mpw_charset ); + byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length ); + logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s", + keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), + (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext ); - byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); - if (siteContextBytes != null) - sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); - logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); + byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); + if (keyContextBytes != null) + sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes ); + logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); - byte[] sitePasswordSeedBytes = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo ); - int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length]; - for (int i = 0; i < sitePasswordSeedBytes.length; ++i) { + byte[] masterKey = getKey(); + logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) ); + byte[] sitePasswordSeedBytes = mpw_digest.of( masterKey, sitePasswordInfo ); + logger.trc( " => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) ); + + return sitePasswordSeedBytes; + } + + @Override + public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, + @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) { + + byte[] siteKey = siteKey( siteName, siteCounter, keyPurpose, keyContext ); + int[] sitePasswordSeed = new int[siteKey.length]; + for (int i = 0; i < siteKey.length; ++i) { ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN ); - Arrays.fill( buf.array(), (byte) ((sitePasswordSeedBytes[i] > 0)? 0x00: 0xFF) ); + Arrays.fill( buf.array(), (byte) ((siteKey[i] > 0)? 0x00: 0xFF) ); buf.position( 2 ); - buf.put( sitePasswordSeedBytes[i] ).rewind(); + buf.put( siteKey[i] ).rewind(); sitePasswordSeed[i] = buf.getInt() & 0xFFFF; } - logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) ); + logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() ); + logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() ); + logger.trc( "resultParam: %s", resultParam ); + + // Determine the template. Preconditions.checkState( sitePasswordSeed.length > 0 ); int templateIndex = sitePasswordSeed[0]; - MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex ); - logger.trc( "type %s, template: %s", siteType, template.getTemplateString() ); + MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex ); + logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() ); + // Encode the password from the seed using the template. StringBuilder password = new StringBuilder( template.length() ); for (int i = 0; i < template.length(); ++i) { int characterIndex = sitePasswordSeed[i + 1]; MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); - logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex, - sitePasswordSeed[i + 1], passwordCharacter ); + logger.trc( " - class: %c, index: %5u (0x%02hX) => character: %c", + characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter ); password.append( passwordCharacter ); } + logger.trc( " => password: %s", password ); return password.toString(); } @Override - protected byte[] bytesForInt(final int number) { - return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number ).array(); + public String siteState(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, + @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) { + + Preconditions.checkNotNull( resultParam ); + Preconditions.checkArgument( !resultParam.isEmpty() ); + + try { + // Encrypt + ByteBuffer plainText = mpw_charset.encode( CharBuffer.wrap( resultParam ) ); + byte[] cipherBuf = CryptUtils.encrypt( plainText.array(), getKey(), true ); + logger.trc( "cipherBuf: %zu bytes = %s", cipherBuf.length, CodeUtils.encodeHex( cipherBuf ) ); + + // Base64-encode + String cipherText = Verify.verifyNotNull( CryptUtils.encodeBase64( cipherBuf ) ); + logger.trc( "b64 encoded -> cipherText: %s", cipherText ); + + return cipherText; + } + catch (final IllegalBlockSizeException e) { + throw logger.bug( e ); + } } @Override - protected byte[] bytesForInt(@Nonnull final UnsignedInteger number) { - return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MPConstant.mpw_byteOrder ).putInt( number.intValue() ).array(); + protected byte[] bytesForInt(final int number) { + return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( mpw_byteOrder ).putInt( number ).array(); + } + + @Override + protected byte[] bytesForInt(final UnsignedInteger number) { + return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( mpw_byteOrder ).putInt( number.intValue() ).array(); } @Override protected byte[] idForBytes(final byte[] bytes) { - return MPConstant.mpw_hash.of( bytes ); + return mpw_hash.of( bytes ); } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java index 47d1a17d..3dcfad27 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java @@ -23,14 +23,13 @@ import com.google.common.primitives.Bytes; import com.google.common.primitives.UnsignedInteger; import com.lyndir.lhunath.opal.system.*; import com.lyndir.lhunath.opal.system.logging.Logger; -import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * bugs: - * - V2: miscounted the byte-length fromInt multi-byte full names. - * - V1: miscounted the byte-length fromInt multi-byte site names. + * - V2: miscounted the byte-length for multi-byte full names. + * - V1: miscounted the byte-length for multi-byte site names. * * @author lhunath, 2014-08-30 */ @@ -50,53 +49,33 @@ public class MasterKeyV1 extends MasterKeyV0 { } @Override - public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter, - final MPSiteVariant siteVariant, @Nullable final String siteContext) { - Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated ); - Preconditions.checkArgument( !siteName.isEmpty() ); + public String siteResult(final String siteName, final UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, + @Nullable final String keyContext, final MPResultType resultType, @Nullable final String resultParam) { - logger.trc( "siteName: %s", siteName ); - logger.trc( "siteCounter: %d", siteCounter.longValue() ); - logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant ); - logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType ); + byte[] sitePasswordSeed = siteKey( siteName, siteCounter, keyPurpose, keyContext ); - if (siteCounter.longValue() == 0) - siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout ); - - String siteScope = siteVariant.getScope(); - byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset ); - byte[] siteNameLengthBytes = bytesForInt( siteName.length() ); - byte[] siteCounterBytes = bytesForInt( siteCounter ); - byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset ); - byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length ); - logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "": siteContext ); - logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), - siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), - (siteContextBytes == null)? "(null)": siteContext ); - - byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); - if (siteContextBytes != null) - sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); - logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); - - byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo ); - logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) ); + logger.trc( "-- mpw_siteResult (algorithm: %u)", getAlgorithmVersion().toInt() ); + logger.trc( "resultType: %d (%s)", resultType.toInt(), resultType.getShortName() ); + logger.trc( "resultParam: %s", resultParam ); + // Determine the template. Preconditions.checkState( sitePasswordSeed.length > 0 ); int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign. - MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex ); - logger.trc( "type %s, template: %s", siteType, template.getTemplateString() ); + MPTemplate template = resultType.getTemplateAtRollingIndex( templateIndex ); + logger.trc( "template: %u => %s", templateIndex, template.getTemplateString() ); + // Encode the password from the seed using the template. StringBuilder password = new StringBuilder( template.length() ); for (int i = 0; i < template.length(); ++i) { int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign. MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); - logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex, - sitePasswordSeed[i + 1], passwordCharacter ); + logger.trc( " - class: %c, index: %3u (0x%02hhX) => character: %c", + characterClass.getIdentifier(), characterIndex, sitePasswordSeed[i + 1], passwordCharacter ); password.append( passwordCharacter ); } + logger.trc( " => password: %s", password ); return password.toString(); } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java index e5472414..9ad93303 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java @@ -23,13 +23,12 @@ import com.google.common.primitives.Bytes; import com.google.common.primitives.UnsignedInteger; import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.logging.Logger; -import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * bugs: - * - V2: miscounted the byte-length fromInt multi-byte full names. + * - V2: miscounted the byte-length for multi-byte full names. * * @author lhunath, 2014-08-30 */ @@ -49,54 +48,43 @@ public class MasterKeyV2 extends MasterKeyV1 { } @Override - public String encode(@Nonnull final String siteName, final MPSiteType siteType, @Nonnull UnsignedInteger siteCounter, - final MPSiteVariant siteVariant, @Nullable final String siteContext) { - Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated ); + protected byte[] siteKey(final String siteName, UnsignedInteger siteCounter, final MPKeyPurpose keyPurpose, + @Nullable final String keyContext) { Preconditions.checkArgument( !siteName.isEmpty() ); + logger.trc( "-- mpw_siteKey (algorithm: %u)", getAlgorithmVersion().toInt() ); logger.trc( "siteName: %s", siteName ); - logger.trc( "siteCounter: %d", siteCounter.longValue() ); - logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant ); - logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType ); + logger.trc( "siteCounter: %d", siteCounter ); + logger.trc( "keyPurpose: %d (%s)", keyPurpose.toInt(), keyPurpose.getShortName() ); + logger.trc( "keyContext: %s", keyContext ); + String keyScope = keyPurpose.getScope(); + logger.trc( "keyScope: %s", keyScope ); + + // OTP counter value. if (siteCounter.longValue() == 0) - siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MPConstant.mpw_counter_timeout * 1000)) * MPConstant.mpw_counter_timeout ); + siteCounter = UnsignedInteger.valueOf( (System.currentTimeMillis() / (MasterKeyV0.mpw_otp_window * 1000)) * MasterKeyV0.mpw_otp_window ); - String siteScope = siteVariant.getScope(); - byte[] siteNameBytes = siteName.getBytes( MPConstant.mpw_charset ); + // Calculate the site seed. + byte[] siteNameBytes = siteName.getBytes( MasterKeyV0.mpw_charset ); byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length ); byte[] siteCounterBytes = bytesForInt( siteCounter ); - byte[] siteContextBytes = ((siteContext == null) || siteContext.isEmpty())? null: siteContext.getBytes( MPConstant.mpw_charset ); - byte[] siteContextLengthBytes = bytesForInt( (siteContextBytes == null)? 0: siteContextBytes.length ); - logger.trc( "site scope: %s, context: %s", siteScope, (siteContextBytes == null)? "": siteContext ); - logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), - siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), - (siteContextBytes == null)? "(null)": siteContext ); + byte[] keyContextBytes = ((keyContext == null) || keyContext.isEmpty())? null: keyContext.getBytes( MasterKeyV0.mpw_charset ); + byte[] keyContextLengthBytes = (keyContextBytes == null)? null: bytesForInt( keyContextBytes.length ); + logger.trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s", + keyScope, CodeUtils.encodeHex( siteNameLengthBytes ), siteName, CodeUtils.encodeHex( siteCounterBytes ), + (keyContextLengthBytes == null)? null: CodeUtils.encodeHex( keyContextLengthBytes ), keyContext ); - byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MPConstant.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); - if (siteContextBytes != null) - sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); - logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); + byte[] sitePasswordInfo = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes ); + if (keyContextBytes != null) + sitePasswordInfo = Bytes.concat( sitePasswordInfo, keyContextLengthBytes, keyContextBytes ); + logger.trc( " => siteSalt.id: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); - byte[] sitePasswordSeed = MPConstant.mpw_digest.of( getKey(), sitePasswordInfo ); - logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) ); + byte[] masterKey = getKey(); + logger.trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )", (Object) idForBytes( masterKey ) ); + byte[] sitePasswordSeedBytes = MasterKeyV0.mpw_digest.of( masterKey, sitePasswordInfo ); + logger.trc( " => siteKey.id: %s", (Object) idForBytes( sitePasswordSeedBytes ) ); - Preconditions.checkState( sitePasswordSeed.length > 0 ); - int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign. - MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex ); - logger.trc( "type %s, template: %s", siteType, template.getTemplateString() ); - - StringBuilder password = new StringBuilder( template.length() ); - for (int i = 0; i < template.length(); ++i) { - int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign. - MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); - char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); - logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex, - sitePasswordSeed[i + 1], passwordCharacter ); - - password.append( passwordCharacter ); - } - - return password.toString(); + return sitePasswordSeedBytes; } } diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java index 041af542..5baacbb5 100644 --- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java +++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java @@ -18,6 +18,7 @@ package com.lyndir.masterpassword; +import com.google.common.base.Preconditions; import com.google.common.primitives.Bytes; import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.logging.Logger; @@ -51,19 +52,37 @@ public class MasterKeyV3 extends MasterKeyV2 { @Nullable @Override protected byte[] deriveKey(final char[] masterPassword) { - byte[] fullNameBytes = getFullName().getBytes( MPConstant.mpw_charset ); + Preconditions.checkArgument( masterPassword.length > 0 ); + + String fullName = getFullName(); + byte[] fullNameBytes = fullName.getBytes( MasterKeyV0.mpw_charset ); byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length ); + ByteBuffer mpBytesBuf = MasterKeyV0.mpw_charset.encode( CharBuffer.wrap( masterPassword ) ); - String mpKeyScope = MPSiteVariant.Password.getScope(); - byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MPConstant.mpw_charset ), fullNameLengthBytes, fullNameBytes ); - logger.trc( "key scope: %s", mpKeyScope ); - logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) ); + logger.trc( "-- mpw_masterKey (algorithm: %u)", getAlgorithmVersion().toInt() ); + logger.trc( "fullName: %s", fullName ); + logger.trc( "masterPassword.id: %s", (Object) idForBytes( mpBytesBuf.array() ) ); - ByteBuffer mpBytesBuf = MPConstant.mpw_charset.encode( CharBuffer.wrap( masterPassword ) ); + String keyScope = MPKeyPurpose.Password.getScope(); + logger.trc( "keyScope: %s", keyScope ); + + // Calculate the master key salt. + logger.trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s", + keyScope, CodeUtils.encodeHex( fullNameLengthBytes ), fullName ); + byte[] masterKeySalt = Bytes.concat( keyScope.getBytes( MasterKeyV0.mpw_charset ), fullNameLengthBytes, fullNameBytes ); + logger.trc( " => masterKeySalt.id: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) ); + + // Calculate the master key. + logger.trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )", + MasterKeyV0.scrypt_N, MasterKeyV0.scrypt_r, MasterKeyV0.scrypt_p ); byte[] mpBytes = new byte[mpBytesBuf.remaining()]; mpBytesBuf.get( mpBytes, 0, mpBytes.length ); Arrays.fill( mpBytesBuf.array(), (byte) 0 ); + byte[] masterKey = scrypt( masterKeySalt, mpBytes ); // TODO: Why not mpBytesBuf.array()? + Arrays.fill( masterKeySalt, (byte) 0 ); + Arrays.fill( mpBytes, (byte) 0 ); + logger.trc( " => masterKey.id: %s", (masterKey == null)? null: (Object) idForBytes( masterKey ) ); - return scrypt( masterKeySalt, mpBytes ); + return masterKey; } } diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java index e23a03fe..872f5bc9 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSite.java @@ -14,50 +14,50 @@ import org.joda.time.Instant; */ public class MPSite { - public static final MPSiteType DEFAULT_TYPE = MPSiteType.GeneratedLong; + public static final MPResultType DEFAULT_TYPE = MPResultType.GeneratedLong; public static final UnsignedInteger DEFAULT_COUNTER = UnsignedInteger.valueOf( 1 ); private final MPUser user; private MasterKey.Version algorithmVersion; private Instant lastUsed; private String siteName; - private MPSiteType siteType; + private MPResultType resultType; private UnsignedInteger siteCounter; private int uses; private String loginName; public MPSite(final MPUser user, final String siteName) { - this( user, siteName, DEFAULT_TYPE, DEFAULT_COUNTER ); + this( user, siteName, DEFAULT_COUNTER, DEFAULT_TYPE ); } - public MPSite(final MPUser user, final String siteName, final MPSiteType siteType, final UnsignedInteger siteCounter) { + public MPSite(final MPUser user, final String siteName, final UnsignedInteger siteCounter, final MPResultType resultType) { this.user = user; this.algorithmVersion = MasterKey.Version.CURRENT; this.lastUsed = new Instant(); this.siteName = siteName; - this.siteType = siteType; + this.resultType = resultType; this.siteCounter = siteCounter; } protected MPSite(final MPUser user, final MasterKey.Version algorithmVersion, final Instant lastUsed, final String siteName, - final MPSiteType siteType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName, + final MPResultType resultType, final UnsignedInteger siteCounter, final int uses, @Nullable final String loginName, @Nullable final String importContent) { this.user = user; this.algorithmVersion = algorithmVersion; this.lastUsed = lastUsed; this.siteName = siteName; - this.siteType = siteType; + this.resultType = resultType; this.siteCounter = siteCounter; this.uses = uses; this.loginName = loginName; } public String resultFor(final MasterKey masterKey) { - return resultFor( masterKey, MPSiteVariant.Password, null ); + return resultFor( masterKey, MPKeyPurpose.Password, null ); } - public String resultFor(final MasterKey masterKey, final MPSiteVariant variant, @Nullable final String context) { - return masterKey.encode( siteName, siteType, siteCounter, variant, context ); + public String resultFor(final MasterKey masterKey, final MPKeyPurpose purpose, @Nullable final String context) { + return masterKey.siteResult( siteName, siteCounter, purpose, context, resultType, null ); } public MPUser getUser() { @@ -94,12 +94,12 @@ public class MPSite { this.siteName = siteName; } - public MPSiteType getSiteType() { - return siteType; + public MPResultType getResultType() { + return resultType; } - public void setSiteType(final MPSiteType siteType) { - this.siteType = siteType; + public void setResultType(final MPResultType resultType) { + this.resultType = resultType; } public UnsignedInteger getSiteCounter() { diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java index 6c109004..5210c91d 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java @@ -64,7 +64,7 @@ public class MPSiteMarshaller { header.append( "# Full Name: " ).append( user.getFullName() ).append( '\n' ); header.append( "# Avatar: " ).append( user.getAvatar() ).append( '\n' ); header.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' ); - header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' ); +// header.append( "# Version: " ).append( MasterKey.Version.CURRENT.toBundleVersion() ).append( '\n' ); header.append( "# Algorithm: " ).append( MasterKey.Version.CURRENT.toInt() ).append( '\n' ); header.append( "# Default Type: " ).append( user.getDefaultType().getType() ).append( '\n' ); header.append( "# Passwords: " ).append( this.contentMode.name() ).append( '\n' ); @@ -82,7 +82,7 @@ public class MPSiteMarshaller { rfc3339.print( site.getLastUsed() ), // lastUsed site.getUses(), // uses strf( "%d:%d:%d", // - site.getSiteType().getType(), // type + site.getResultType().getType(), // type site.getAlgorithmVersion().toInt(), // algorithm site.getSiteCounter().intValue() ), // counter ifNotNullElse( site.getLoginName(), "" ), // loginName diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java index c0dbc4bc..69a8f928 100644 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPSiteUnmarshaller.java @@ -11,7 +11,7 @@ import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.util.ConversionUtils; import com.lyndir.lhunath.opal.system.util.NNOperation; -import com.lyndir.masterpassword.MPSiteType; +import com.lyndir.masterpassword.MPResultType; import com.lyndir.masterpassword.MasterKey; import java.io.*; import java.util.List; @@ -54,13 +54,13 @@ public class MPSiteUnmarshaller { @Nonnull public static MPSiteUnmarshaller unmarshall(@Nonnull final List lines) { - byte[] keyID = null; - String fullName = null; - int mpVersion = 0, importFormat = 0, avatar = 0; - boolean clearContent = false, headerStarted = false; - MPSiteType defaultType = MPSiteType.GeneratedLong; - MPSiteUnmarshaller marshaller = null; - final ImmutableList.Builder sites = ImmutableList.builder(); + byte[] keyID = null; + String fullName = null; + int mpVersion = 0, importFormat = 0, avatar = 0; + boolean clearContent = false, headerStarted = false; + MPResultType defaultType = MPResultType.GeneratedLong; + MPSiteUnmarshaller marshaller = null; + final ImmutableList.Builder sites = ImmutableList.builder(); for (final String line : lines) // Header delimitor. @@ -92,7 +92,7 @@ public class MPSiteUnmarshaller { else if ("Passwords".equalsIgnoreCase( name )) clearContent = "visible".equalsIgnoreCase( value ); else if ("Default Type".equalsIgnoreCase( name )) - defaultType = MPSiteType.forType( ConversionUtils.toIntegerNN( value ) ); + defaultType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) ); } } } @@ -110,7 +110,7 @@ public class MPSiteUnmarshaller { } protected MPSiteUnmarshaller(final int importFormat, final int mpVersion, final String fullName, final byte[] keyID, final int avatar, - final MPSiteType defaultType, final boolean clearContent) { + final MPResultType defaultType, final boolean clearContent) { this.importFormat = importFormat; this.mpVersion = mpVersion; this.clearContent = clearContent; @@ -131,7 +131,7 @@ public class MPSiteUnmarshaller { MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), // rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), // siteMatcher.group( 5 ), // - MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, // + MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), MPSite.DEFAULT_COUNTER, // ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), // null, // siteMatcher.group( 6 ) ); @@ -142,7 +142,7 @@ public class MPSiteUnmarshaller { MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( siteMatcher.group( 4 ).replace( ":", "" ) ) ), // rfc3339.parseDateTime( siteMatcher.group( 1 ) ).toInstant(), // siteMatcher.group( 7 ), // - MPSiteType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), + MPResultType.forType( ConversionUtils.toIntegerNN( siteMatcher.group( 3 ) ) ), UnsignedInteger.valueOf( siteMatcher.group( 5 ).replace( ":", "" ) ), // ConversionUtils.toIntegerNN( siteMatcher.group( 2 ) ), // siteMatcher.group( 6 ), // diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java index e75806e2..ef08c212 100755 --- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java +++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPUser.java @@ -5,7 +5,7 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.lyndir.lhunath.opal.system.CodeUtils; -import com.lyndir.masterpassword.MPSiteType; +import com.lyndir.masterpassword.MPResultType; import com.lyndir.masterpassword.MasterKey; import java.util.*; import javax.annotation.Nonnull; @@ -25,7 +25,7 @@ public class MPUser implements Comparable { private byte[] keyID; private final MasterKey.Version algorithmVersion; private int avatar; - private MPSiteType defaultType; + private MPResultType defaultType; private ReadableInstant lastUsed; public MPUser(final String fullName) { @@ -33,11 +33,11 @@ public class MPUser implements Comparable { } public MPUser(final String fullName, @Nullable final byte[] keyID) { - this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPSiteType.GeneratedLong, new DateTime() ); + this( fullName, keyID, MasterKey.Version.CURRENT, 0, MPResultType.GeneratedLong, new DateTime() ); } public MPUser(final String fullName, @Nullable final byte[] keyID, final MasterKey.Version algorithmVersion, final int avatar, - final MPSiteType defaultType, final ReadableInstant lastUsed) { + final MPResultType defaultType, final ReadableInstant lastUsed) { this.fullName = fullName; this.keyID = (keyID == null)? null: keyID.clone(); this.algorithmVersion = algorithmVersion; @@ -107,11 +107,11 @@ public class MPUser implements Comparable { this.avatar = avatar; } - public MPSiteType getDefaultType() { + public MPResultType getDefaultType() { return defaultType; } - public void setDefaultType(final MPSiteType defaultType) { + public void setDefaultType(final MPResultType defaultType) { this.defaultType = defaultType; } diff --git a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java index 8eaa6fea..4af802d0 100644 --- a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java +++ b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java @@ -99,12 +99,12 @@ public class MPTestSuite implements Callable { currentCase.siteName = text; if ("siteCounter".equals( qName )) currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text ); - if ("siteType".equals( qName )) - currentCase.siteType = text; - if ("siteVariant".equals( qName )) - currentCase.siteVariant = text; - if ("siteContext".equals( qName )) - currentCase.siteContext = text; + if ("resultType".equals( qName )) + currentCase.resultType = text; + if ("keyPurpose".equals( qName )) + currentCase.keyPurpose = text; + if ("keyContext".equals( qName )) + currentCase.keyContext = text; if ("result".equals( qName )) currentCase.result = text; } @@ -173,8 +173,9 @@ public class MPTestSuite implements Callable { @Override public Boolean apply(@Nonnull final MPTests.Case testCase) { MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() ); - String sitePassword = masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), - testCase.getSiteVariant(), testCase.getSiteContext() ); + String sitePassword = masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(), + testCase.getKeyContext(), testCase.getResultType(), + null ); return testCase.getResult().equals( sitePassword ); } diff --git a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java index c7ba7fe2..1c79c157 100644 --- a/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java +++ b/core/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java @@ -66,18 +66,18 @@ public class MPTests { public static class Case { - String identifier; - String parent; - Integer algorithm; - String fullName; - String masterPassword; - String keyID; - String siteName; + String identifier; + String parent; + Integer algorithm; + String fullName; + String masterPassword; + String keyID; + String siteName; UnsignedInteger siteCounter; - String siteType; - String siteVariant; - String siteContext; - String result; + String resultType; + String keyPurpose; + String keyContext; + String result; private transient Case parentCase; @@ -130,25 +130,25 @@ public class MPTests { return checkNotNull( parentCase.siteCounter ); } } ); - siteType = ifNotNullElse( siteType, new NNSupplier() { + resultType = ifNotNullElse( resultType, new NNSupplier() { @Nonnull @Override public String get() { - return checkNotNull( parentCase.siteType ); + return checkNotNull( parentCase.resultType ); } } ); - siteVariant = ifNotNullElse( siteVariant, new NNSupplier() { + keyPurpose = ifNotNullElse( keyPurpose, new NNSupplier() { @Nonnull @Override public String get() { - return checkNotNull( parentCase.siteVariant ); + return checkNotNull( parentCase.keyPurpose ); } } ); - siteContext = ifNotNullElse( siteContext, new NNSupplier() { + keyContext = ifNotNullElse( keyContext, new NNSupplier() { @Nonnull @Override public String get() { - return (parentCase == null)? "": checkNotNull( parentCase.siteContext ); + return (parentCase == null)? "": checkNotNull( parentCase.keyContext ); } } ); result = ifNotNullElse( result, new NNSupplier() { @@ -200,18 +200,18 @@ public class MPTests { } @Nonnull - public MPSiteType getSiteType() { - return MPSiteType.forName( checkNotNull( siteType ) ); + public MPResultType getResultType() { + return MPResultType.forName( checkNotNull( resultType ) ); } @Nonnull - public MPSiteVariant getSiteVariant() { - return MPSiteVariant.forName( checkNotNull( siteVariant ) ); + public MPKeyPurpose getKeyPurpose() { + return MPKeyPurpose.forName( checkNotNull( keyPurpose ) ); } @Nonnull - public String getSiteContext() { - return checkNotNull( siteContext ); + public String getKeyContext() { + return checkNotNull( keyContext ); } @Nonnull diff --git a/core/java/tests/src/main/resources/mpw_tests.xml b/core/java/tests/src/main/resources/mpw_tests.xml index 36465d8b..01f56a9a 100644 --- a/core/java/tests/src/main/resources/mpw_tests.xml +++ b/core/java/tests/src/main/resources/mpw_tests.xml @@ -7,7 +7,7 @@ 98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302 masterpasswordapp.com 1 - GeneratedLong + Long Authentication @@ -33,12 +33,12 @@ Identification - GeneratedName + Name wohzaqage Recovery - GeneratedPhrase + Phrase xin diyjiqoja hubu @@ -46,31 +46,31 @@ xogx tem cegyiva jab - GeneratedMaximum + Maximum W6@692^B1#&@gVdSdLZ@ - GeneratedMedium + Medium Jej2$Quv - GeneratedBasic + Basic WAo2xIg6 - GeneratedShort + Short Jej2 - GeneratedPIN + PIN 7662 - GeneratedName + Name jejraquvo - GeneratedPhrase + Phrase jejr quv cabsibu tam @@ -99,12 +99,12 @@ Identification - GeneratedName + Name wohzaqage Recovery - GeneratedPhrase + Phrase xin diyjiqoja hubu @@ -112,31 +112,31 @@ xogx tem cegyiva jab - GeneratedMaximum + Maximum W6@692^B1#&@gVdSdLZ@ - GeneratedMedium + Medium Jej2$Quv - GeneratedBasic + Basic WAo2xIg6 - GeneratedShort + Short Jej2 - GeneratedPIN + PIN 7662 - GeneratedName + Name jejraquvo - GeneratedPhrase + Phrase jejr quv cabsibu tam @@ -165,12 +165,12 @@ Identification - GeneratedName + Name wohzaqage Recovery - GeneratedPhrase + Phrase xin diyjiqoja hubu @@ -178,31 +178,31 @@ xogx tem cegyiva jab - GeneratedMaximum + Maximum W6@692^B1#&@gVdSdLZ@ - GeneratedMedium + Medium Jej2$Quv - GeneratedBasic + Basic WAo2xIg6 - GeneratedShort + Short Jej2 - GeneratedPIN + PIN 7662 - GeneratedName + Name jejraquvo - GeneratedPhrase + Phrase jejr quv cabsibu tam @@ -231,12 +231,12 @@ Identification - GeneratedName + Name lozwajave Recovery - GeneratedPhrase + Phrase miy lirfijoja dubu @@ -244,31 +244,31 @@ movm bex gevrica jaf - GeneratedMaximum + Maximum w1!3bA3icmRAc)SS@lwl - GeneratedMedium + Medium Fej7]Jug - GeneratedBasic + Basic wvH7irC1 - GeneratedShort + Short Fej7 - GeneratedPIN + PIN 2117 - GeneratedName + Name fejrajugo - GeneratedPhrase + Phrase fejr jug gabsibu bax diff --git a/core/java/tests/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java b/core/java/tests/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java index 803c6a00..82144f3c 100644 --- a/core/java/tests/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java +++ b/core/java/tests/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java @@ -55,8 +55,9 @@ public class MasterKeyTest { MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() ); assertEquals( - masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), - testCase.getSiteVariant(), testCase.getSiteContext() ), + masterKey.siteResult( testCase.getSiteName(), testCase.getSiteCounter(), testCase.getKeyPurpose(), + testCase.getKeyContext(), testCase.getResultType(), + null ), testCase.getResult(), "[testEncode] Failed test case: " + testCase ); return true; @@ -101,8 +102,9 @@ public class MasterKeyTest { MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ); masterKey.invalidate(); - masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(), - defaultCase.getSiteVariant(), defaultCase.getSiteContext() ); + masterKey.siteResult( defaultCase.getSiteName(), defaultCase.getSiteCounter(), defaultCase.getKeyPurpose(), + defaultCase.getKeyContext(), defaultCase.getResultType(), + null ); fail( "[testInvalidate] Master key should have been invalidated, but was still usable." ); } diff --git a/gradle/pom.xml b/gradle/pom.xml index a4bbcf3e..d9abad1e 100644 --- a/gradle/pom.xml +++ b/gradle/pom.xml @@ -22,7 +22,6 @@ masterpassword-tests masterpassword-algorithm masterpassword-model - masterpassword-cli masterpassword-gui diff --git a/gradle/settings.gradle b/gradle/settings.gradle index 151e9057..a56506d2 100644 --- a/gradle/settings.gradle +++ b/gradle/settings.gradle @@ -9,9 +9,6 @@ project(':masterpassword-model').projectDir = new File( '../core/java/model' ) include 'masterpassword-tests' project(':masterpassword-tests').projectDir = new File( '../core/java/tests' ) -include 'masterpassword-cli' -project(':masterpassword-cli').projectDir = new File( '../platform-independent/cli-java' ) - include 'masterpassword-gui' project(':masterpassword-gui').projectDir = new File( '../platform-independent/gui-java' ) diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java index 88ae8672..9011ffb5 100644 --- a/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java +++ b/platform-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java @@ -52,10 +52,10 @@ public class EmergencyActivity extends Activity { private static final int PASSWORD_NOTIFICATION = 0; public static final int CLIPBOARD_CLEAR_DELAY = 20 /* s */ * MPConstant.MS_PER_S; - private final Preferences preferences = Preferences.get( this ); - private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() ); - private final ImmutableList allSiteTypes = ImmutableList.copyOf( MPSiteType.forClass( MPSiteTypeClass.Generated ) ); - private final ImmutableList allVersions = ImmutableList.copyOf( MasterKey.Version.values() ); + private final Preferences preferences = Preferences.get( this ); + private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() ); + private final ImmutableList allResultTypes = ImmutableList.copyOf( MPResultType.forClass( MPResultTypeClass.Generated ) ); + private final ImmutableList allVersions = ImmutableList.copyOf( MasterKey.Version.values() ); private ListenableFuture masterKeyFuture; @@ -71,8 +71,8 @@ public class EmergencyActivity extends Activity { @BindView(R.id.siteNameField) EditText siteNameField; - @BindView(R.id.siteTypeButton) - Button siteTypeButton; + @BindView(R.id.resultTypeButton) + Button resultTypeButton; @BindView(R.id.counterField) Button siteCounterButton; @@ -131,15 +131,15 @@ public class EmergencyActivity extends Activity { updateSitePassword(); } } ); - siteTypeButton.setOnClickListener( new View.OnClickListener() { + resultTypeButton.setOnClickListener( new View.OnClickListener() { @Override public void onClick(final View v) { @SuppressWarnings("SuspiciousMethodCalls") - MPSiteType siteType = - allSiteTypes.get( (allSiteTypes.indexOf( siteTypeButton.getTag() ) + 1) % allSiteTypes.size() ); - preferences.setDefaultSiteType( siteType ); - siteTypeButton.setTag( siteType ); - siteTypeButton.setText( siteType.getShortName() ); + MPResultType resultType = + allResultTypes.get( (allResultTypes.indexOf( resultTypeButton.getTag() ) + 1) % allResultTypes.size() ); + preferences.setDefaultResultType( resultType ); + resultTypeButton.setTag( resultType ); + resultTypeButton.setText( resultType.getShortName() ); updateSitePassword(); } } ); @@ -220,9 +220,9 @@ public class EmergencyActivity extends Activity { forgetPasswordField.setChecked( preferences.isForgetPassword() ); maskPasswordField.setChecked( preferences.isMaskPassword() ); sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null ); - MPSiteType defaultSiteType = preferences.getDefaultSiteType(); - siteTypeButton.setTag( defaultSiteType ); - siteTypeButton.setText( defaultSiteType.getShortName() ); + MPResultType defaultResultType = preferences.getDefaultResultType(); + resultTypeButton.setTag( defaultResultType ); + resultTypeButton.setText( defaultResultType.getShortName() ); MasterKey.Version defaultVersion = preferences.getDefaultVersion(); siteVersionButton.setTag( defaultVersion ); siteVersionButton.setText( defaultVersion.name() ); @@ -313,9 +313,9 @@ public class EmergencyActivity extends Activity { } private void updateSitePassword() { - final String siteName = siteNameField.getText().toString(); - final MPSiteType type = (MPSiteType) siteTypeButton.getTag(); - final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() ); + final String siteName = siteNameField.getText().toString(); + final MPResultType type = (MPResultType) resultTypeButton.getTag(); + final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() ); if ((masterKeyFuture == null) || siteName.isEmpty() || (type == null)) { sitePasswordField.setText( "" ); @@ -332,7 +332,7 @@ public class EmergencyActivity extends Activity { @Override public void run() { try { - sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null ); + sitePassword = masterKeyFuture.get().siteResult( siteName, counter, MPKeyPurpose.Password, null, type, null ); runOnUiThread( new Runnable() { @Override diff --git a/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java b/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java index 23e5b2e4..3d4d047a 100644 --- a/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java +++ b/platform-android/src/main/java/com/lyndir/masterpassword/Preferences.java @@ -38,7 +38,7 @@ public final class Preferences { private static final String PREF_FORGET_PASSWORD = "forgetPassword"; private static final String PREF_MASK_PASSWORD = "maskPassword"; private static final String PREF_FULL_NAME = "fullName"; - private static final String PREF_SITE_TYPE = "siteType"; + private static final String PREF_RESULT_TYPE = "resultType"; private static final String PREF_ALGORITHM_VERSION = "algorithmVersion"; private static Preferences instance; @@ -138,20 +138,20 @@ public final class Preferences { return prefs().getString( PREF_FULL_NAME, "" ); } - public boolean setDefaultSiteType(@Nonnull final MPSiteType value) { - if (getDefaultSiteType() == value) + public boolean setDefaultResultType(final MPResultType value) { + if (getDefaultResultType() == value) return false; - prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply(); + prefs().edit().putInt( PREF_RESULT_TYPE, value.ordinal() ).apply(); return true; } @Nonnull - public MPSiteType getDefaultSiteType() { - return MPSiteType.values()[prefs().getInt( PREF_SITE_TYPE, MPSiteType.GeneratedLong.ordinal() )]; + public MPResultType getDefaultResultType() { + return MPResultType.values()[prefs().getInt( PREF_RESULT_TYPE, MPResultType.GeneratedLong.ordinal() )]; } - public boolean setDefaultVersion(@Nonnull final MasterKey.Version value) { + public boolean setDefaultVersion(final MasterKey.Version value) { if (getDefaultVersion() == value) return false; diff --git a/platform-android/src/main/res/layout/activity_emergency.xml b/platform-android/src/main/res/layout/activity_emergency.xml index d2312d33..2c0fb79b 100644 --- a/platform-android/src/main/res/layout/activity_emergency.xml +++ b/platform-android/src/main/res/layout/activity_emergency.xml @@ -105,7 +105,7 @@ android:id="@id/sitePasswordField" android:layout_width="match_parent" android:layout_height="wrap_content" - android:nextFocusForward="@+id/siteTypeButton" + android:nextFocusForward="@+id/resultTypeButton" android:gravity="center" android:background="@android:color/transparent" android:textColor="#FFFFFF" @@ -157,7 +157,7 @@ android:gravity="center">