diff --git a/.gitignore b/.gitignore
index 6f94bd72..45a384f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# OS-Specific junk.
.DS_Store
+Thumbs.db
# IntelliJ
/MasterPassword/Java/.idea
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
index e206d70d..4c94b2e6 100644
--- a/.idea/encodings.xml
+++ b/.idea/encodings.xml
@@ -1,5 +1,10 @@
-
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/External/iOS/Crashlytics.framework/Versions/A/Crashlytics b/External/iOS/Crashlytics.framework/Versions/A/Crashlytics
index ec79a372..df0fe475 100644
Binary files a/External/iOS/Crashlytics.framework/Versions/A/Crashlytics and b/External/iOS/Crashlytics.framework/Versions/A/Crashlytics differ
diff --git a/External/iOS/Crashlytics.framework/Versions/A/Headers/Crashlytics.h b/External/iOS/Crashlytics.framework/Versions/A/Headers/Crashlytics.h
index 7102c5c1..9683173c 100644
--- a/External/iOS/Crashlytics.framework/Versions/A/Headers/Crashlytics.h
+++ b/External/iOS/Crashlytics.framework/Versions/A/Headers/Crashlytics.h
@@ -218,3 +218,8 @@ OBJC_EXTERN void CLSNSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id )crash;
@end
+
+/**
+ * `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, simply use `Crashlytics()`
+ */
+#define CrashlyticsKit [Crashlytics sharedInstance]
diff --git a/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist b/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
index ab0c6383..a10d5efb 100644
--- a/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
+++ b/External/iOS/Crashlytics.framework/Versions/A/Resources/Info.plist
@@ -15,13 +15,13 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.2.5
+ 2.2.9
CFBundleSupportedPlatforms
iPhoneOS
CFBundleVersion
- 40
+ 44
DTPlatformName
iphoneos
MinimumOSVersion
diff --git a/External/iOS/Crashlytics.framework/run b/External/iOS/Crashlytics.framework/run
index b34d4f75..b9211c3a 100755
Binary files a/External/iOS/Crashlytics.framework/run and b/External/iOS/Crashlytics.framework/run differ
diff --git a/External/iOS/Crashlytics.framework/submit b/External/iOS/Crashlytics.framework/submit
index c34d5c2e..32c9ca36 100755
Binary files a/External/iOS/Crashlytics.framework/submit and b/External/iOS/Crashlytics.framework/submit differ
diff --git a/MasterPassword/C/build b/MasterPassword/C/build
index 0a3820d3..948e779a 100755
--- a/MasterPassword/C/build
+++ b/MasterPassword/C/build
@@ -39,7 +39,7 @@ else
fi
# Optional features.
-mpw_color=0 # Colorized Identicon, requires libncurses-dev
+mpw_color=1 # Colorized Identicon, requires libncurses-dev
### DEPENDENCIES
@@ -204,11 +204,11 @@ mpw() {
echo
echo "Building target: $target..."
- CFLAGS=(
+ local CFLAGS=(
# include paths
-I"lib/include"
)
- LDFLAGS=(
+ local LDFLAGS=(
# scrypt
"lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o"
@@ -240,11 +240,11 @@ mpw-bench() {
echo
echo "Building target: $target..."
- CFLAGS=(
+ local CFLAGS=(
# include paths
-I"lib/include"
)
- LDFLAGS=(
+ local LDFLAGS=(
# scrypt
"lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o"
@@ -264,6 +264,9 @@ mpw-bench() {
-l"crypto"
)
+ cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
+ cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
+ cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
echo "done! Now use ./mpw-bench"
@@ -276,13 +279,13 @@ mpw-tests() {
echo
echo "Building target: $target..."
- CFLAGS=(
+ local CFLAGS=(
# include paths
-I"lib/include"
-I"/usr/include/libxml2"
-I"/usr/local/include/libxml2"
)
- LDFLAGS=(
+ local LDFLAGS=(
# scrypt
"lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o"
@@ -296,7 +299,10 @@ mpw-tests() {
-l"crypto" -l"xml2"
)
- cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
+ cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
+ cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
+ cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
+ cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
"${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests"
echo "done! Now use ./mpw-tests"
diff --git a/MasterPassword/C/mpw-algorithm_v0.c b/MasterPassword/C/mpw-algorithm_v0.c
index 98e70e1c..1878772f 100644
--- a/MasterPassword/C/mpw-algorithm_v0.c
+++ b/MasterPassword/C/mpw-algorithm_v0.c
@@ -11,6 +11,7 @@
#include
#include
+#include "mpw-types.h"
#include "mpw-util.h"
#define MP_N 32768
@@ -37,7 +38,8 @@ static const char mpw_characterFromClass_v0(char characterClass, uint16_t seedBy
static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
- trc( "fullName: %s\n", fullName );
+ trc( "algorithm: v%d\n", 0 );
+ trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
@@ -71,11 +73,16 @@ static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
+ trc( "algorithm: v%d\n", 0 );
trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType );
- trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext );
+ trc( "site scope: %s, context: %s\n", siteScope, siteContext? "": siteContext );
+ trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
+ siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
+ mpw_hex_l( htonl( siteCounter ) ),
+ mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
diff --git a/MasterPassword/C/mpw-algorithm_v1.c b/MasterPassword/C/mpw-algorithm_v1.c
index 1a20107f..74923b9f 100644
--- a/MasterPassword/C/mpw-algorithm_v1.c
+++ b/MasterPassword/C/mpw-algorithm_v1.c
@@ -11,6 +11,7 @@
#include
#include
+#include "mpw-types.h"
#include "mpw-util.h"
#define MP_N 32768
@@ -21,7 +22,8 @@
static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
- trc( "fullName: %s\n", fullName );
+ trc( "algorithm: v%d\n", 1 );
+ trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
@@ -55,11 +57,16 @@ static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
+ trc( "algorithm: v%d\n", 1 );
trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType );
- trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext );
+ trc( "site scope: %s, context: %s\n", siteScope, siteContext? "": siteContext );
+ trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
+ siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
+ mpw_hex_l( htonl( siteCounter ) ),
+ mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
diff --git a/MasterPassword/C/mpw-algorithm_v2.c b/MasterPassword/C/mpw-algorithm_v2.c
index 1823c6ff..123d5e66 100644
--- a/MasterPassword/C/mpw-algorithm_v2.c
+++ b/MasterPassword/C/mpw-algorithm_v2.c
@@ -11,6 +11,7 @@
#include
#include
+#include "mpw-types.h"
#include "mpw-util.h"
#define MP_N 32768
@@ -21,7 +22,8 @@
static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
- trc( "fullName: %s\n", fullName );
+ trc( "algorithm: v%d\n", 2 );
+ trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
@@ -55,11 +57,16 @@ static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
+ trc( "algorithm: v%d\n", 2 );
trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType );
- trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext );
+ trc( "site scope: %s, context: %s\n", siteScope, siteContext? "": siteContext );
+ trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
+ siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
+ mpw_hex_l( htonl( siteCounter ) ),
+ mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
diff --git a/MasterPassword/C/mpw-algorithm_v3.c b/MasterPassword/C/mpw-algorithm_v3.c
index 72ba92cb..a9ed37e0 100644
--- a/MasterPassword/C/mpw-algorithm_v3.c
+++ b/MasterPassword/C/mpw-algorithm_v3.c
@@ -11,6 +11,7 @@
#include
#include
+#include "mpw-types.h"
#include "mpw-util.h"
#define MP_N 32768
@@ -21,7 +22,8 @@
static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
- trc( "fullName: %s\n", fullName );
+ trc( "algorithm: v%d\n", 3 );
+ trc( "fullName: %s (%zu)\n", fullName, strlen( fullName ) );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
@@ -55,11 +57,16 @@ static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
+ trc( "algorithm: v%d\n", 3 );
trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType );
- trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "": siteContext );
+ trc( "site scope: %s, context: %s\n", siteScope, siteContext? "": siteContext );
+ trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
+ siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
+ mpw_hex_l( htonl( siteCounter ) ),
+ mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
diff --git a/MasterPassword/C/mpw-cli.c b/MasterPassword/C/mpw-cli.c
index 3f651baa..68f744a4 100644
--- a/MasterPassword/C/mpw-cli.c
+++ b/MasterPassword/C/mpw-cli.c
@@ -25,7 +25,7 @@
static void usage() {
- fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] site\n\n" );
+ fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] [-V version] [-v variant] [-C context] [-h] site\n\n" );
fprintf( stderr, " -u name Specify the full name of the user.\n"
" Defaults to %s in env.\n\n", MP_env_fullname );
fprintf( stderr, " -t type Specify the password's template.\n"
diff --git a/MasterPassword/C/mpw-tests.c b/MasterPassword/C/mpw-tests.c
index 8755bd59..7f4c8981 100644
--- a/MasterPassword/C/mpw-tests.c
+++ b/MasterPassword/C/mpw-tests.c
@@ -21,6 +21,7 @@ int main(int argc, char *const argv[]) {
// Read in the test case.
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
+ uint32_t algorithm = mpw_xmlTestCaseInteger( testCase, "algorithm" );
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
@@ -36,16 +37,20 @@ int main(int argc, char *const argv[]) {
// Run the test case.
fprintf( stdout, "test case %s... ", id );
+ if (!xmlStrlen( result )) {
+ fprintf( stdout, "abstract.\n" );
+ continue;
+ }
// 1. calculate the master key.
const uint8_t *masterKey = mpw_masterKeyForUser(
- (char *)fullName, (char *)masterPassword, MPAlgorithmVersionCurrent );
+ (char *)fullName, (char *)masterPassword, algorithm );
if (!masterKey)
ftl( "Couldn't derive master key." );
// 2. calculate the site password.
const char *sitePassword = mpw_passwordForSite(
- masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, MPAlgorithmVersionCurrent );
+ masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, algorithm );
mpw_free( masterKey, MP_dkLen );
if (!sitePassword)
ftl( "Couldn't derive site password." );
@@ -56,7 +61,7 @@ int main(int argc, char *const argv[]) {
else {
++failedTests;
- fprintf( stdout, "FAILED! (result %s != expected %s)\n", result, sitePassword );
+ fprintf( stdout, "FAILED! (got %s != expected %s)\n", sitePassword, result );
}
// Free test case.
diff --git a/MasterPassword/C/mpw-types.c b/MasterPassword/C/mpw-types.c
index 92d5da5f..9b14d569 100644
--- a/MasterPassword/C/mpw-types.c
+++ b/MasterPassword/C/mpw-types.c
@@ -21,15 +21,17 @@
const MPSiteType mpw_typeWithName(const char *typeName) {
+ // Lower-case and trim optionally leading "Generated" string from typeName to standardize it.
+ size_t stdTypeNameOffset = 0;
size_t stdTypeNameSize = strlen( typeName );
- char stdTypeName[strlen( typeName )];
- if (stdTypeNameSize > strlen( "generated" ))
- strcpy( stdTypeName, typeName + strlen( "generated" ) );
- else
- strcpy( stdTypeName, typeName );
- for (char *tN = stdTypeName; *tN; ++tN)
- *tN = (char)tolower( *tN );
+ 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[stdTypeNameSize] = '\0';
+ // Find what site type is represented by the type name.
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
return MPSiteTypeGeneratedMaximum;
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
@@ -47,11 +49,10 @@ const MPSiteType mpw_typeWithName(const char *typeName) {
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
return MPSiteTypeGeneratedPhrase;
- fprintf( stderr, "Not a generated type name: %s", stdTypeName );
- abort();
+ ftl( "Not a generated type name: %s", stdTypeName );
}
-inline const char **mpw_templatesForType(MPSiteType type, size_t *count) {
+const char **mpw_templatesForType(MPSiteType type, size_t *count) {
if (!(type & MPSiteTypeClassGenerated)) {
ftl( "Not a generated type: %d", type );
@@ -61,42 +62,42 @@ inline const char **mpw_templatesForType(MPSiteType type, size_t *count) {
switch (type) {
case MPSiteTypeGeneratedMaximum: {
- *count = 2;
- return (const char *[]){ "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
+ return alloc_array( *count, const char *,
+ "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
}
case MPSiteTypeGeneratedLong: {
- *count = 21;
- return (const char *[]){ "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
- "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
- "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
- "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
- "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
- "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
- "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" };
+ return alloc_array( *count, const char *,
+ "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
+ "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
+ "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
+ "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
+ "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
+ "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
+ "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
}
case MPSiteTypeGeneratedMedium: {
- *count = 2;
- return (const char *[]){ "CvcnoCvc", "CvcCvcno" };
+ return alloc_array( *count, const char *,
+ "CvcnoCvc", "CvcCvcno" );
}
case MPSiteTypeGeneratedBasic: {
- *count = 3;
- return (const char *[]){ "aaanaaan", "aannaaan", "aaannaaa" };
+ return alloc_array( *count, const char *,
+ "aaanaaan", "aannaaan", "aaannaaa" );
}
case MPSiteTypeGeneratedShort: {
- *count = 1;
- return (const char *[]){"Cvcn"};
+ return alloc_array( *count, const char *,
+ "Cvcn" );
}
case MPSiteTypeGeneratedPIN: {
- *count = 1;
- return (const char *[]){ "nnnn" };
+ return alloc_array( *count, const char *,
+ "nnnn" );
}
case MPSiteTypeGeneratedName: {
- *count = 1;
- return (const char *[]) {"cvccvcvcv"};
+ return alloc_array( *count, const char *,
+ "cvccvcvcv" );
}
case MPSiteTypeGeneratedPhrase: {
- *count = 3;
- return (const char *[]){ "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
+ return alloc_array( *count, const char *,
+ "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
}
default: {
ftl( "Unknown generated type: %d", type );
@@ -113,15 +114,19 @@ const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
if (!count)
return NULL;
- return templates[seedByte % count];
+ char const *template = templates[seedByte % count];
+ free( templates );
+ return template;
}
const MPSiteVariant mpw_variantWithName(const char *variantName) {
- char stdVariantName[strlen( variantName )];
- strcpy( stdVariantName, variantName );
- for (char *vN = stdVariantName; *vN; ++vN)
- *vN = (char)tolower( *vN );
+ // Lower-case and trim optionally leading "generated" string from typeName to standardize it.
+ size_t stdVariantNameSize = strlen( variantName );
+ char stdVariantName[stdVariantNameSize + 1];
+ for (size_t c = 0; c < stdVariantNameSize; ++c)
+ stdVariantName[c] = (char)tolower( variantName[c] );
+ stdVariantName[stdVariantNameSize] = '\0';
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
return MPSiteVariantPassword;
diff --git a/MasterPassword/C/mpw-types.h b/MasterPassword/C/mpw-types.h
index 2b17394d..a476d81c 100644
--- a/MasterPassword/C/mpw-types.h
+++ b/MasterPassword/C/mpw-types.h
@@ -6,6 +6,8 @@
// Copyright (c) 2014 Lyndir. All rights reserved.
//
+#ifndef _MPW_TYPES_H
+#define _MPW_TYPES_H
#include
#include
@@ -73,9 +75,10 @@ const char *mpw_scopeForVariant(MPSiteVariant variant);
const MPSiteType mpw_typeWithName(const char *typeName);
/**
- * @return An array of internal strings that express the templates to use for the given type.
+ * @return A newly allocated array of internal strings that express the templates to use for the given type.
* The amount of elements in the array is stored in count.
* If an unsupported type is given, count will be 0 and will return NULL.
+* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
*/
const char **mpw_templatesForType(MPSiteType type, size_t *count);
/**
@@ -93,3 +96,4 @@ const char *mpw_charactersInClass(char characterClass);
*/
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
+#endif // _MPW_TYPES_H
diff --git a/MasterPassword/C/mpw-util.c b/MasterPassword/C/mpw-util.c
index 864371b3..b50ae37b 100644
--- a/MasterPassword/C/mpw-util.c
+++ b/MasterPassword/C/mpw-util.c
@@ -9,7 +9,12 @@
#include
#include
#include
-#include
+
+#ifdef COLOR
+#include
+#include
+#include
+#endif
#include
#include
@@ -91,20 +96,31 @@ const char *mpw_idForBuf(const void *buf, size_t length) {
return mpw_hex( hash, 32 );
}
-static char *mpw_hex_buf = NULL;
+static char **mpw_hex_buf = NULL;
+static unsigned int mpw_hex_buf_i = 0;
const char *mpw_hex(const void *buf, size_t length) {
- mpw_hex_buf = realloc( mpw_hex_buf, length * 2 + 1 );
- for (size_t kH = 0; kH < length; kH++)
- sprintf( &(mpw_hex_buf[kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
+ if (!mpw_hex_buf) {
+ mpw_hex_buf = malloc( 10 * sizeof( char* ) );
+ for (uint8_t i = 0; i < 10; ++i)
+ mpw_hex_buf[i] = NULL;
+ }
+ mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10;
- return mpw_hex_buf;
+ mpw_hex_buf[mpw_hex_buf_i] = realloc( mpw_hex_buf[mpw_hex_buf_i], length * 2 + 1 );
+ for (size_t kH = 0; kH < length; kH++)
+ sprintf( &(mpw_hex_buf[mpw_hex_buf_i][kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
+
+ return mpw_hex_buf[mpw_hex_buf_i];
+}
+const char *mpw_hex_l(uint32_t number) {
+ return mpw_hex( &number, sizeof( number ) );
}
#ifdef COLOR
static int putvari;
static char *putvarc = NULL;
-static bool istermsetup = false;
+static int istermsetup = 0;
static void initputvar() {
if (putvarc)
free(putvarc);
@@ -165,8 +181,33 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
return identicon;
}
-const size_t mpw_charlen(const char *string) {
+/**
+* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
+*/
+static int mpw_charByteSize(unsigned char utf8Byte) {
- setlocale( LC_ALL, "en_US.UTF-8" );
- return mbstowcs( NULL, string, strlen( string ) );
+ if (!utf8Byte)
+ return 0;
+ if ((utf8Byte & 0x80) == 0)
+ return 1;
+ if ((utf8Byte & 0xC0) != 0xC0)
+ return 0;
+ if ((utf8Byte & 0xE0) == 0xC0)
+ return 2;
+ if ((utf8Byte & 0xF0) == 0xE0)
+ return 3;
+ if ((utf8Byte & 0xF8) == 0xF0)
+ return 4;
+
+ return 0;
+}
+
+const size_t mpw_charlen(const char *utf8String) {
+
+ size_t charlen = 0;
+ char *remainingString = (char *)utf8String;
+ for (int charByteSize; (charByteSize = mpw_charByteSize( *remainingString )); remainingString += charByteSize)
+ ++charlen;
+
+ return charlen;
}
diff --git a/MasterPassword/C/mpw-util.h b/MasterPassword/C/mpw-util.h
index 44ac0644..815706ad 100644
--- a/MasterPassword/C/mpw-util.h
+++ b/MasterPassword/C/mpw-util.h
@@ -25,6 +25,14 @@
//// Buffers and memory.
+#define alloc_array(_count, _type, ...) ({ \
+ _type stackElements[] = { __VA_ARGS__ }; \
+ _count = sizeof( stackElements ) / sizeof( _type ); \
+ _type *allocElements = malloc( sizeof( stackElements ) ); \
+ memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
+ allocElements; \
+ })
+
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
void mpw_pushBuf(
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
@@ -58,6 +66,7 @@ uint8_t const *mpw_hmac_sha256(
/** Encode a buffer as a string of hexadecimal characters.
* @return A C-string in a reused buffer, do not free or store it. */
const char *mpw_hex(const void *buf, size_t length);
+const char *mpw_hex_l(uint32_t number);
/** Encode a fingerprint for a buffer.
* @return A C-string in a reused buffer, do not free or store it. */
const char *mpw_idForBuf(const void *buf, size_t length);
@@ -67,4 +76,5 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword);
//// String utilities.
-const size_t mpw_charlen(const char *string);
+/** @return The amount of display characters in the given UTF-8 string. */
+const size_t mpw_charlen(const char *utf8String);
diff --git a/MasterPassword/Java/masterpassword-algorithm/pom.xml b/MasterPassword/Java/masterpassword-algorithm/pom.xml
index 0ea494d3..ea6ecebb 100644
--- a/MasterPassword/Java/masterpassword-algorithm/pom.xml
+++ b/MasterPassword/Java/masterpassword-algorithm/pom.xml
@@ -13,7 +13,6 @@
Master Password Algorithm Implementation
The implementation of the Master Password algorithm
- com.lyndir.masterpassword
masterpassword-algorithm
jar
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java
index f78d2a44..b6f31eae 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteType.java
@@ -6,6 +6,7 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
+import org.jetbrains.annotations.Contract;
/**
@@ -147,7 +148,8 @@ public enum MPSiteType {
*
* @return The type registered with the given name.
*/
- public static MPSiteType forName(final String name) {
+ @Contract("!null -> !null, null -> null")
+ public static MPSiteType forName(@Nullable final String name) {
if (name == null)
return null;
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java
index f87f32cd..b6039f4b 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPSiteVariant.java
@@ -3,6 +3,8 @@ package com.lyndir.masterpassword;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List;
+import javax.annotation.Nullable;
+import org.jetbrains.annotations.Contract;
/**
@@ -65,7 +67,8 @@ public enum MPSiteVariant {
*
* @return The variant registered with the given name.
*/
- public static MPSiteVariant forName(final String name) {
+ @Contract("!null -> !null, null -> null")
+ public static MPSiteVariant forName(@Nullable final String name) {
if (name == null)
return null;
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplate.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplate.java
index 6a7c3d8d..b605ccab 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplate.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplate.java
@@ -3,10 +3,8 @@ package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import java.util.List;
-import java.util.Map;
/**
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplateCharacterClass.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplateCharacterClass.java
index 2838034f..54aa04cb 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplateCharacterClass.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPTemplateCharacterClass.java
@@ -1,8 +1,6 @@
package com.lyndir.masterpassword;
import com.lyndir.lhunath.opal.system.logging.Logger;
-import com.lyndir.lhunath.opal.system.util.MetaObject;
-import com.lyndir.lhunath.opal.system.util.ObjectMeta;
/**
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
index 886063e0..84390db9 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKey.java
@@ -54,7 +54,7 @@ public abstract class MasterKey {
@Nullable
protected abstract byte[] deriveKey(final char[] masterPassword);
- protected abstract Version getAlgorithm();
+ public abstract Version getAlgorithmVersion();
@NotNull
public String getFullName() {
@@ -63,18 +63,18 @@ public abstract class MasterKey {
}
@Nonnull
- protected byte[] getMasterKey() {
+ protected byte[] getKey() {
return Preconditions.checkNotNull( masterKey );
}
public byte[] getKeyID() {
- return idForBytes( getMasterKey() );
+ return idForBytes( getKey() );
}
- public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
- @Nullable final String siteContext);
+ public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, int siteCounter,
+ final MPSiteVariant siteVariant, @Nullable final String siteContext);
public boolean isValid() {
return masterKey != null;
@@ -95,8 +95,12 @@ public abstract class MasterKey {
long start = System.currentTimeMillis();
masterKey = deriveKey( masterPassword );
- logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
- (System.currentTimeMillis() - start) / 1000D );
+
+ if (masterKey == null)
+ logger.dbg( "masterKey calculation failed after %.2fs.", (System.currentTimeMillis() - start) / 1000D );
+ else
+ logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
+ (System.currentTimeMillis() - start) / 1000D );
return this;
}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
index 040db693..c5671911 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV0.java
@@ -41,7 +41,7 @@ public class MasterKeyV0 extends MasterKey {
}
@Override
- protected Version getAlgorithm() {
+ public Version getAlgorithmVersion() {
return Version.V0;
}
@@ -89,29 +89,37 @@ public class MasterKeyV0 extends MasterKey {
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
- byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
+ byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
- logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "": siteContext );
+ 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 ),
- siteContext == null? "(null)": siteContext );
+ siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
- byte[] sitePasswordSeed = MP_mac.of( getMasterKey(), sitePasswordInfo );
- logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
+ byte[] sitePasswordSeedBytes = MP_mac.of( getKey(), sitePasswordInfo );
+ int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
+ for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
+ ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
+ Arrays.fill( buf.array(), sitePasswordSeedBytes[i] > 0? (byte)0x00: (byte) 0xFF );
+ buf.position( 2 );
+ buf.put( sitePasswordSeedBytes[i] ).rewind();
+ sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
+ }
+ logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
- int templateIndex = sitePasswordSeed[0] & 0xFFFF;
+ int templateIndex = sitePasswordSeed[0];
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] & 0xFFFF;
+ 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,
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
index 6889863e..bcb9d58d 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV1.java
@@ -1,15 +1,9 @@
package com.lyndir.masterpassword;
-import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
-import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.security.GeneralSecurityException;
import javax.annotation.Nullable;
@@ -30,7 +24,7 @@ public class MasterKeyV1 extends MasterKeyV0 {
}
@Override
- protected Version getAlgorithm() {
+ public Version getAlgorithmVersion() {
return Version.V1;
}
@@ -52,19 +46,19 @@ public class MasterKeyV1 extends MasterKeyV0 {
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter );
- byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
+ byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
- logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "": siteContext );
+ 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 ),
- siteContext == null? "(null)": siteContext );
+ siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
- byte[] sitePasswordSeed = MP_mac.of( getMasterKey(), sitePasswordInfo );
+ byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
index 5ebc0012..7da2856a 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV2.java
@@ -23,7 +23,7 @@ public class MasterKeyV2 extends MasterKeyV1 {
}
@Override
- protected Version getAlgorithm() {
+ public Version getAlgorithmVersion() {
return Version.V2;
}
@@ -45,19 +45,19 @@ public class MasterKeyV2 extends MasterKeyV1 {
byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
byte[] siteCounterBytes = bytesForInt( siteCounter );
- byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
+ byte[] siteContextBytes = siteContext == null || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
- logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "": siteContext );
+ 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 ),
- siteContext == null? "(null)": siteContext );
+ siteContextBytes == null? "(null)": siteContext );
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
- byte[] sitePasswordSeed = MP_mac.of( getMasterKey(), sitePasswordInfo );
+ byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java
index 0c9cfc6b..7d593f1e 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MasterKeyV3.java
@@ -26,7 +26,7 @@ public class MasterKeyV3 extends MasterKeyV2 {
}
@Override
- protected Version getAlgorithm() {
+ public Version getAlgorithmVersion() {
return Version.V3;
}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MPWTests.java b/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MPWTests.java
index de9b804e..5673bda6 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MPWTests.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MPWTests.java
@@ -1,10 +1,10 @@
package com.lyndir.masterpassword;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
-import com.lyndir.lhunath.opal.system.util.NNSupplier;
-import com.lyndir.lhunath.opal.system.util.NSupplier;
+import com.lyndir.lhunath.opal.system.util.*;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -25,8 +25,9 @@ public class MPWTests {
@XmlElement(name = "case")
private List cases;
+ @Nonnull
public List getCases() {
- return cases;
+ return checkNotNull( cases );
}
public Case getCase(String identifier) {
@@ -45,6 +46,8 @@ public class MPWTests {
@XmlAttribute
private String parent;
@XmlElement
+ private String algorithm;
+ @XmlElement
private String fullName;
@XmlElement
private String masterPassword;
@@ -65,76 +68,86 @@ public class MPWTests {
private transient Case parentCase;
- public void setTests(MPWTests tests) {
+ public void initializeParentHierarchy(MPWTests tests) {
if (parent != null) {
parentCase = tests.getCase( parent );
- fullName = ifNotNullElse( fullName, new NNSupplier() {
- @Nonnull
- @Override
- public String get() {
- return parentCase.getFullName();
- }
- } );
- masterPassword = ifNotNullElse( masterPassword, new NNSupplier() {
- @Nonnull
- @Override
- public String get() {
- return new String( parentCase.getMasterPassword() );
- }
- } );
- keyID = ifNotNullElse( keyID, new NNSupplier() {
- @Nonnull
- @Override
- public String get() {
- return parentCase.getKeyID();
- }
- } );
- siteName = ifNotNullElse( siteName, new NNSupplier() {
- @Nonnull
- @Override
- public String get() {
- return parentCase.getSiteName();
- }
- } );
- siteCounter = ifNotNullElse( siteCounter, new NNSupplier() {
- @Nonnull
- @Override
- public Integer get() {
- return parentCase.getSiteCounter();
- }
- } );
- siteType = ifNotNullElse( siteType, new NNSupplier() {
- @Nonnull
- @Override
- public String get() {
- return parentCase.getSiteType().name();
- }
- } );
- siteVariant = ifNotNullElse( siteVariant, new NNSupplier() {
- @Nonnull
- @Override
- public String get() {
- return parentCase.getSiteVariant().name();
- }
- } );
- siteContext = ifNotNullElseNullable( siteContext, new NSupplier() {
- @Nonnull
- @Override
- public String get() {
- return parentCase.getSiteContext();
- }
- } );
- result = ifNotNullElse( result, new NNSupplier() {
- @Nonnull
- @Override
- public String get() {
- return parentCase.getResult();
- }
- } );
+ parentCase.initializeParentHierarchy( tests );
}
+
+ algorithm = ifNotNullElse( algorithm, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return checkNotNull( parentCase.algorithm );
+ }
+ } );
+ fullName = ifNotNullElse( fullName, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return checkNotNull( parentCase.fullName );
+ }
+ } );
+ masterPassword = ifNotNullElse( masterPassword, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return checkNotNull( parentCase.masterPassword );
+ }
+ } );
+ keyID = ifNotNullElse( keyID, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return checkNotNull( parentCase.keyID );
+ }
+ } );
+ siteName = ifNotNullElse( siteName, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return checkNotNull( parentCase.siteName );
+ }
+ } );
+ siteCounter = ifNotNullElse( siteCounter, new NNSupplier() {
+ @Nonnull
+ @Override
+ public Integer get() {
+ return checkNotNull( parentCase.siteCounter );
+ }
+ } );
+ siteType = ifNotNullElse( siteType, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return checkNotNull( parentCase.siteType );
+ }
+ } );
+ siteVariant = ifNotNullElse( siteVariant, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return checkNotNull( parentCase.siteVariant );
+ }
+ } );
+ siteContext = ifNotNullElse( siteContext, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return parentCase == null? "": checkNotNull( parentCase.siteContext );
+ }
+ } );
+ result = ifNotNullElse( result, new NNSupplier() {
+ @Nonnull
+ @Override
+ public String get() {
+ return parentCase == null? "": checkNotNull( parentCase.result );
+ }
+ } );
}
+ @Nonnull
public String getIdentifier() {
return identifier;
}
@@ -144,40 +157,53 @@ public class MPWTests {
return parentCase;
}
+ @Nonnull
+ public MasterKey.Version getAlgorithm() {
+ return MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( algorithm ) );
+ }
+
+ @Nonnull
public String getFullName() {
- return fullName;
+ return checkNotNull( fullName );
}
+ @Nonnull
public char[] getMasterPassword() {
- return masterPassword.toCharArray();
+ return checkNotNull( masterPassword ).toCharArray();
}
+ @Nonnull
public String getKeyID() {
- return keyID;
+ return checkNotNull( keyID );
}
+ @Nonnull
public String getSiteName() {
- return siteName;
+ return checkNotNull( siteName );
}
public int getSiteCounter() {
- return siteCounter;
+ return ifNotNullElse( siteCounter, 1 );
}
+ @Nonnull
public MPSiteType getSiteType() {
- return MPSiteType.forName( siteType );
+ return MPSiteType.forName( checkNotNull( siteType ) );
}
+ @Nonnull
public MPSiteVariant getSiteVariant() {
- return MPSiteVariant.forName( siteVariant );
+ return MPSiteVariant.forName( checkNotNull( siteVariant ) );
}
+ @Nonnull
public String getSiteContext() {
- return siteContext;
+ return checkNotNull( siteContext );
}
+ @Nonnull
public String getResult() {
- return result;
+ return checkNotNull( result );
}
@Override
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java b/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java
index 6b04ac50..55978452 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java
+++ b/MasterPassword/Java/masterpassword-algorithm/src/test/java/com/lyndir/masterpassword/MasterKeyTest.java
@@ -5,6 +5,7 @@ import static org.testng.Assert.*;
import com.google.common.io.Resources;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
+import com.lyndir.lhunath.opal.system.util.StringUtils;
import java.net.URL;
import javax.xml.bind.JAXBContext;
import org.testng.annotations.BeforeMethod;
@@ -26,7 +27,7 @@ public class MasterKeyTest {
URL testCasesResource = Resources.getResource( "mpw_tests.xml" );
tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource );
for (MPWTests.Case testCase : tests.getCases())
- testCase.setTests( tests );
+ testCase.initializeParentHierarchy( tests );
defaultCase = tests.getCase( MPWTests.ID_DEFAULT );
}
@@ -35,10 +36,15 @@ public class MasterKeyTest {
throws Exception {
for (MPWTests.Case testCase : tests.getCases()) {
- MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
+ if (testCase.getResult().isEmpty())
+ continue;
+
+ logger.inf( "Running test case: %s [testEncode]", testCase.getIdentifier() );
+ MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
assertEquals(
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
+ logger.inf( "passed!" );
}
}
@@ -55,8 +61,13 @@ public class MasterKeyTest {
throws Exception {
for (MPWTests.Case testCase : tests.getCases()) {
+ if (testCase.getResult().isEmpty())
+ continue;
+
+ logger.inf( "Running test case: %s [testGetKeyID]", testCase.getIdentifier() );
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
+ logger.inf( "passed!" );
}
}
diff --git a/MasterPassword/Java/masterpassword-algorithm/src/test/resources/mpw_tests.xml b/MasterPassword/Java/masterpassword-algorithm/src/test/resources/mpw_tests.xml
index 09ca5a21..86d24fc7 100644
--- a/MasterPassword/Java/masterpassword-algorithm/src/test/resources/mpw_tests.xml
+++ b/MasterPassword/Java/masterpassword-algorithm/src/test/resources/mpw_tests.xml
@@ -1,5 +1,7 @@
+
+
Robert Lee Mitchell
banana colored duckling
98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302
@@ -7,67 +9,271 @@
1
GeneratedLong
Password
+
+
+
+
+
+ 3
Jejr5[RepuSosp
-
+
⛄
1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8
NopaDajh8=Fene
-
+
⛄
351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08
QesuHirv5-Xepl
-
+
⛄
LiheCuwhSerz6)
-
+
Login
GeneratedName
wohzaqage
-
+
Answer
GeneratedPhrase
xin diyjiqoja hubu
-
+
question
xogx tem cegyiva jab
-
+
GeneratedMaximum
W6@692^B1#&@gVdSdLZ@
-
+
GeneratedMedium
Jej2$Quv
-
+
GeneratedBasic
WAo2xIg6
-
+
GeneratedShort
Jej2
-
+
GeneratedPIN
7662
-
+
GeneratedName
jejraquvo
-
+
GeneratedPhrase
jejr quv cabsibu tam
-
+
4294967295
XambHoqo6[Peni
+
+
+
+ 2
+ Jejr5[RepuSosp
+
+
+ ⛄
+ 1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8
+ WaqoGuho2[Xaxw
+
+
+ ⛄
+ 351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08
+ QesuHirv5-Xepl
+
+
+ ⛄
+ LiheCuwhSerz6)
+
+
+ Login
+ GeneratedName
+ wohzaqage
+
+
+ Answer
+ GeneratedPhrase
+ xin diyjiqoja hubu
+
+
+ question
+ xogx tem cegyiva jab
+
+
+ GeneratedMaximum
+ W6@692^B1#&@gVdSdLZ@
+
+
+ GeneratedMedium
+ Jej2$Quv
+
+
+ GeneratedBasic
+ WAo2xIg6
+
+
+ GeneratedShort
+ Jej2
+
+
+ GeneratedPIN
+ 7662
+
+
+ GeneratedName
+ jejraquvo
+
+
+ GeneratedPhrase
+ jejr quv cabsibu tam
+
+
+ 4294967295
+ XambHoqo6[Peni
+
+
+
+
+ 1
+ Jejr5[RepuSosp
+
+
+ ⛄
+ 1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8
+ WaqoGuho2[Xaxw
+
+
+ ⛄
+ 351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08
+ QesuHirv5-Xepl
+
+
+ ⛄
+ WawiYarp2@Kodh
+
+
+ Login
+ GeneratedName
+ wohzaqage
+
+
+ Answer
+ GeneratedPhrase
+ xin diyjiqoja hubu
+
+
+ question
+ xogx tem cegyiva jab
+
+
+ GeneratedMaximum
+ W6@692^B1#&@gVdSdLZ@
+
+
+ GeneratedMedium
+ Jej2$Quv
+
+
+ GeneratedBasic
+ WAo2xIg6
+
+
+ GeneratedShort
+ Jej2
+
+
+ GeneratedPIN
+ 7662
+
+
+ GeneratedName
+ jejraquvo
+
+
+ GeneratedPhrase
+ jejr quv cabsibu tam
+
+
+ 4294967295
+ XambHoqo6[Peni
+
+
+
+
+ 0
+ Feji5@ReduWosh
+
+
+ ⛄
+ 1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8
+ HajrYudo7@Mamh
+
+
+ ⛄
+ 351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08
+ MewmDini0]Meho
+
+
+ ⛄
+ HahiVana2@Nole
+
+
+ Login
+ GeneratedName
+ lozwajave
+
+
+ Answer
+ GeneratedPhrase
+ miy lirfijoja dubu
+
+
+ question
+ movm bex gevrica jaf
+
+
+ GeneratedMaximum
+ w1!3bA3icmRAc)SS@lwl
+
+
+ GeneratedMedium
+ Fej7]Jug
+
+
+ GeneratedBasic
+ wvH7irC1
+
+
+ GeneratedShort
+ Fej7
+
+
+ GeneratedPIN
+ 2117
+
+
+ GeneratedName
+ fejrajugo
+
+
+ GeneratedPhrase
+ fejr jug gabsibu bax
+
+
+ 4294967295
+ QateDojh1@Hecn
+
diff --git a/MasterPassword/Java/masterpassword-android/AndroidManifest.xml b/MasterPassword/Java/masterpassword-android/AndroidManifest.xml
index f4e88b25..b77b5ff8 100644
--- a/MasterPassword/Java/masterpassword-android/AndroidManifest.xml
+++ b/MasterPassword/Java/masterpassword-android/AndroidManifest.xml
@@ -2,7 +2,7 @@
+ android:versionName="2.2">
Master Password Android
An Android application to the Master Password algorithm
- com.lyndir.masterpassword
masterpassword-android
apk
@@ -30,7 +29,7 @@
false
- 19
+ 21
@@ -39,9 +38,32 @@
- sign
+ release
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+
+
+
+ false
+
+
+
+
+
+ manifest-update
+ process-resources
+
+ manifest-update
+
+
+ true
+
+
+
+
org.apache.maven.plugins
maven-jarsigner-plugin
@@ -54,14 +76,14 @@
package
true
-
+
target/*.apk
release.jks
${env.PASSWORD}
${env.PASSWORD}
- android
+ masterpassword-android
-sigalgMD5withRSA
-digestalgSHA1
@@ -70,16 +92,6 @@
-
- com.jayway.maven.plugins.android.generation2
- android-maven-plugin
- true
-
-
- false
-
-
-
diff --git a/MasterPassword/Java/masterpassword-android/release.jks b/MasterPassword/Java/masterpassword-android/release.jks
new file mode 120000
index 00000000..35870df1
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-android/release.jks
@@ -0,0 +1 @@
+/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks
\ No newline at end of file
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar0.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar0.png
deleted file mode 100644
index 6df3b1c1..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar0.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar1.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar1.png
deleted file mode 100644
index 242619c0..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar1.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar10.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar10.png
deleted file mode 100644
index 35f80e5c..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar10.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar11.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar11.png
deleted file mode 100644
index 4562f3a6..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar11.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar12.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar12.png
deleted file mode 100644
index bb3a3b11..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar12.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar13.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar13.png
deleted file mode 100644
index 4541ab58..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar13.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar14.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar14.png
deleted file mode 100644
index 080b1d12..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar14.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar15.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar15.png
deleted file mode 100644
index bddce79c..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar15.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar16.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar16.png
deleted file mode 100644
index 8b318240..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar16.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar17.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar17.png
deleted file mode 100644
index 7abd6f8d..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar17.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar18.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar18.png
deleted file mode 100644
index d3b2380d..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar18.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar2.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar2.png
deleted file mode 100644
index e130c614..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar2.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar3.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar3.png
deleted file mode 100644
index c28868a3..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar3.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar4.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar4.png
deleted file mode 100644
index 9c5b7ed1..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar4.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar5.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar5.png
deleted file mode 100644
index 61773fbd..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar5.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar6.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar6.png
deleted file mode 100644
index de110435..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar6.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar7.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar7.png
deleted file mode 100644
index 47164fd7..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar7.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar8.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar8.png
deleted file mode 100644
index e6593d21..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar8.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar9.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar9.png
deleted file mode 100644
index b9b7656a..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avatar9.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avataradd.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avataradd.png
deleted file mode 100644
index be68d16e..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/avataradd.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/background.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/background.png
index dad596a0..7386cfde 100644
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/background.png and b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/background.png differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/icon.png b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/icon.png
index 97d4cbc2..6fb0d1f5 100644
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/icon.png and b/MasterPassword/Java/masterpassword-android/res/drawable-mdpi/icon.png differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar0.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar0.png
deleted file mode 100644
index 306ca9b5..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar0.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar1.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar1.png
deleted file mode 100644
index e6b92536..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar1.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar10.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar10.png
deleted file mode 100644
index a59c61b1..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar10.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar11.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar11.png
deleted file mode 100644
index 0372e8c7..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar11.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar12.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar12.png
deleted file mode 100644
index 36903d25..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar12.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar13.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar13.png
deleted file mode 100644
index 6b71b615..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar13.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar14.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar14.png
deleted file mode 100644
index 42b41be4..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar14.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar15.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar15.png
deleted file mode 100644
index a50ec0c4..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar15.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar16.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar16.png
deleted file mode 100644
index a468c6b0..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar16.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar17.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar17.png
deleted file mode 100644
index 57d5cba1..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar17.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar18.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar18.png
deleted file mode 100644
index f24242a1..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar18.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar2.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar2.png
deleted file mode 100644
index 46bc4f5c..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar2.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar3.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar3.png
deleted file mode 100644
index 7f7ff918..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar3.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar4.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar4.png
deleted file mode 100644
index 4c59a0d1..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar4.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar5.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar5.png
deleted file mode 100644
index bd6efe2c..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar5.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar6.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar6.png
deleted file mode 100644
index b759e9ff..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar6.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar7.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar7.png
deleted file mode 100644
index 8810bb21..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar7.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar8.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar8.png
deleted file mode 100644
index 7e38a4bc..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar8.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar9.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar9.png
deleted file mode 100644
index d05b86b8..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avatar9.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avataradd.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avataradd.png
deleted file mode 100644
index 3e91b6dc..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/avataradd.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/background.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/background.png
index 7bcaa643..afd7ff5c 100644
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/background.png and b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/background.png differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/icon.png b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/icon.png
index 14e49c6b..f7b31eac 100644
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/icon.png and b/MasterPassword/Java/masterpassword-android/res/drawable-xhdpi/icon.png differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xxxhdpi/background.png b/MasterPassword/Java/masterpassword-android/res/drawable-xxxhdpi/background.png
new file mode 100644
index 00000000..3213fac7
Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/res/drawable-xxxhdpi/background.png differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable-xxxhdpi/icon.png b/MasterPassword/Java/masterpassword-android/res/drawable-xxxhdpi/icon.png
new file mode 100644
index 00000000..4bfaabe8
Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/res/drawable-xxxhdpi/icon.png differ
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable/divider20.xml b/MasterPassword/Java/masterpassword-android/res/drawable/divider20.xml
deleted file mode 100644
index c48ea6c5..00000000
--- a/MasterPassword/Java/masterpassword-android/res/drawable/divider20.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/MasterPassword/Java/masterpassword-android/res/drawable/double_.png b/MasterPassword/Java/masterpassword-android/res/drawable/double_.png
deleted file mode 100644
index 842e9bec..00000000
Binary files a/MasterPassword/Java/masterpassword-android/res/drawable/double_.png and /dev/null differ
diff --git a/MasterPassword/Java/masterpassword-android/res/layout/activity_users.xml b/MasterPassword/Java/masterpassword-android/res/layout/activity_users.xml
deleted file mode 100644
index 91922ec3..00000000
--- a/MasterPassword/Java/masterpassword-android/res/layout/activity_users.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/MasterPassword/Java/masterpassword-android/res/layout/view_user_avatar.xml b/MasterPassword/Java/masterpassword-android/res/layout/view_user_avatar.xml
deleted file mode 100644
index 025fae64..00000000
--- a/MasterPassword/Java/masterpassword-android/res/layout/view_user_avatar.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/MasterPassword/Java/masterpassword-android/res/values/strings.xml b/MasterPassword/Java/masterpassword-android/res/values/strings.xml
index 9890392c..82556446 100644
--- a/MasterPassword/Java/masterpassword-android/res/values/strings.xml
+++ b/MasterPassword/Java/masterpassword-android/res/values/strings.xml
@@ -1,7 +1,6 @@
Master Password
- User Avatar
Remember
Forget on close
Hide password
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
index 170ff6b6..939af580 100644
--- a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
+++ b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java
@@ -205,7 +205,7 @@ public class EmergencyActivity extends Activity {
final MasterKey.Version version = (MasterKey.Version) siteVersionField.getSelectedItem();
try {
if (fullName.hashCode() == hc_userName && Arrays.hashCode( masterPassword ) == hc_masterPassword &&
- masterKeyFuture != null && masterKeyFuture.get().getAlgorithm() == version)
+ masterKeyFuture != null && masterKeyFuture.get().getAlgorithmVersion() == version)
return;
}
catch (InterruptedException | ExecutionException e) {
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/Res.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/Res.java
index 0f8824e7..5ac37a42 100644
--- a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/Res.java
+++ b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/Res.java
@@ -1,7 +1,6 @@
package com.lyndir.masterpassword;
import android.content.res.Resources;
-import android.graphics.Paint;
import android.graphics.Typeface;
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/UsersActivity.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/UsersActivity.java
deleted file mode 100644
index a6f347b1..00000000
--- a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/UsersActivity.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.lyndir.masterpassword;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.widget.LinearLayout;
-import butterknife.ButterKnife;
-import butterknife.InjectView;
-import com.lyndir.masterpassword.model.Avatar;
-import com.lyndir.masterpassword.model.User;
-import com.lyndir.masterpassword.view.AvatarView;
-
-
-public class UsersActivity extends Activity {
-
- @InjectView(R.id.users)
- LinearLayout users;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate( savedInstanceState );
- setContentView( R.layout.activity_users );
- ButterKnife.inject( this );
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- AvatarView avatarView = new AvatarView( this );
- avatarView.setUser( new User( "Maarten Billemont", Avatar.EIGHT ) );
- users.addView( avatarView );
- }
-}
-
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/model/Avatar.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/model/Avatar.java
deleted file mode 100644
index d551aa71..00000000
--- a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/model/Avatar.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.lyndir.masterpassword.model;
-
-import com.lyndir.masterpassword.R;
-
-
-/**
- * @author lhunath, 2014-08-20
- */
-public enum Avatar {
- ZERO( R.drawable.avatar0 ),
- ONE( R.drawable.avatar1 ),
- TWO( R.drawable.avatar2 ),
- THREE( R.drawable.avatar3 ),
- FOUR( R.drawable.avatar4 ),
- FIVE( R.drawable.avatar5 ),
- SIX( R.drawable.avatar6 ),
- SEVEN( R.drawable.avatar7 ),
- EIGHT( R.drawable.avatar8 ),
- NINE( R.drawable.avatar9 ),
- TEN( R.drawable.avatar10 ),
- ELEVEN( R.drawable.avatar11 ),
- TWELVE( R.drawable.avatar12 ),
- THIRTEEN( R.drawable.avatar13 ),
- FOURTEEN( R.drawable.avatar14 ),
- FIFTEEN( R.drawable.avatar15 ),
- SIXTEEN( R.drawable.avatar16 ),
- SEVENTEEN( R.drawable.avatar17 ),
- EIGHTEEN( R.drawable.avatar18 );
-
- private final int imageResource;
-
- Avatar(final int imageResource) {
- this.imageResource = imageResource;
- }
-
- public int getImageResource() {
- return imageResource;
- }
-}
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/model/User.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/model/User.java
deleted file mode 100644
index 9568f9fc..00000000
--- a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/model/User.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.lyndir.masterpassword.model;
-
-import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
-
-import java.util.Objects;
-
-
-/**
- * @author lhunath, 2014-08-20
- */
-public class User {
-
- private String name;
- private Avatar avatar;
-
- public User(final String name, final Avatar avatar) {
- this.name = name;
- this.avatar = avatar;
- }
-
- public String getName() {
- return name;
- }
-
- public Avatar getAvatar() {
- return avatar;
- }
-
- @Override
- public boolean equals(final Object obj) {
- return this == obj || obj instanceof User && name.equals( ((User) obj).name );
- }
-
- @Override
- public int hashCode() {
- return name.hashCode();
- }
-
- @Override
- public String toString() {
- return strf( "{User: %s}", name );
- }
-}
diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/view/AvatarView.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/view/AvatarView.java
deleted file mode 100644
index 6febb0c0..00000000
--- a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/view/AvatarView.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.lyndir.masterpassword.view;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-import com.lyndir.masterpassword.R;
-import com.lyndir.masterpassword.model.User;
-
-
-/**
- * @author lhunath, 2014-08-20
- */
-public class AvatarView extends FrameLayout {
-
- private final TextView userName;
-
- public AvatarView(final Context context) {
- super( context );
-
- addView( userName = (TextView) LayoutInflater.from( context ).inflate( R.layout.view_user_avatar, this, false ) );
- }
-
- public void setUser(User user) {
- userName.setText( user.getName() );
- userName.setCompoundDrawables( null, getResources().getDrawable( user.getAvatar().getImageResource() ), null, null );
- }
-}
diff --git a/MasterPassword/Java/masterpassword-cli/pom.xml b/MasterPassword/Java/masterpassword-cli/pom.xml
index 93f99018..69df3bf0 100644
--- a/MasterPassword/Java/masterpassword-cli/pom.xml
+++ b/MasterPassword/Java/masterpassword-cli/pom.xml
@@ -13,7 +13,6 @@
Master Password CLI
A CLI interface to the Master Password algorithm
- com.lyndir.masterpassword
masterpassword-cli
jar
@@ -37,7 +36,7 @@
prepare-package
-
+
diff --git a/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java b/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
index 94944564..b818c988 100644
--- a/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
+++ b/MasterPassword/Java/masterpassword-cli/src/main/java/com/lyndir/masterpassword/CLI.java
@@ -45,7 +45,7 @@ public class CLI {
throws IOException {
// Read information from the environment.
- char[] masterPassword = null;
+ char[] masterPassword;
String siteName = null, context = null;
String userName = System.getenv( ENV_USERNAME );
String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" );
diff --git a/MasterPassword/Java/masterpassword-gui/pom.xml b/MasterPassword/Java/masterpassword-gui/pom.xml
index 63aab06a..c1fd58fb 100644
--- a/MasterPassword/Java/masterpassword-gui/pom.xml
+++ b/MasterPassword/Java/masterpassword-gui/pom.xml
@@ -13,7 +13,6 @@
Master Password GUI
A GUI interface to the Master Password algorithm
- com.lyndir.masterpassword
masterpassword-gui
jar
@@ -67,6 +66,67 @@
+
+
+ release
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+
+
+
+ false
+
+
+
+
+
+ manifest-update
+ process-resources
+
+ manifest-update
+
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jarsigner-plugin
+
+
+ signing
+
+ sign
+
+ package
+ true
+
+
+
+ target/*.jar
+
+ release.jks
+ ${env.PASSWORD}
+ ${env.PASSWORD}
+ masterpassword-desktop
+
+ -sigalgMD5withRSA
+ -digestalgSHA1
+
+
+
+
+
+
+
+
+
+
diff --git a/MasterPassword/Java/masterpassword-gui/release.jks b/MasterPassword/Java/masterpassword-gui/release.jks
new file mode 120000
index 00000000..35870df1
--- /dev/null
+++ b/MasterPassword/Java/masterpassword-gui/release.jks
@@ -0,0 +1 @@
+/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks
\ No newline at end of file
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java
index 2e985d19..2b4a6e10 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java
@@ -21,8 +21,7 @@ import com.google.common.base.Charsets;
import com.google.common.io.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.TypeUtils;
-import com.lyndir.masterpassword.MasterKey;
-import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
+
import java.io.*;
import java.net.URI;
import java.net.URL;
@@ -41,7 +40,7 @@ public class GUI implements UnlockFrame.SignInCallback {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( GUI.class );
- private UnlockFrame unlockFrame = new UnlockFrame( this );
+ private final UnlockFrame unlockFrame = new UnlockFrame( this );
private PasswordFrame passwordFrame;
public static void main(final String[] args)
@@ -50,7 +49,13 @@ public class GUI implements UnlockFrame.SignInCallback {
if (Config.get().checkForUpdates())
checkUpdate();
- TypeUtils.newInstance( AppleGUI.class ).or( new GUI() ).open();
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
+ catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
+ }
+
+ TypeUtils.newInstance( "com.lyndir.masterpassword.gui.platform.mac.AppleGUI" ).or( new GUI() ).open();
}
private static void checkUpdate() {
@@ -82,7 +87,7 @@ public class GUI implements UnlockFrame.SignInCallback {
}
}
- void open() {
+ protected void open() {
SwingUtilities.invokeLater( new Runnable() {
@Override
public void run() {
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java
index dbd9d4a4..b78aac31 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java
@@ -1,6 +1,7 @@
package com.lyndir.masterpassword.gui;
import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
import com.google.common.collect.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.model.MPUser;
@@ -12,6 +13,7 @@ import javax.annotation.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
+import javax.swing.plaf.metal.MetalComboBoxEditor;
/**
@@ -50,6 +52,15 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
userField.setFont( Res.valueFont().deriveFont( 12f ) );
userField.addItemListener( this );
userField.addActionListener( this );
+ userField.setEditor(new MetalComboBoxEditor() {
+ @Override
+ protected JTextField createEditorComponent() {
+ JTextField editorComponents = Components.textField();
+ editorComponents.setForeground(Color.red);
+ return editorComponents;
+ }
+ });
+
add( userField );
add( Components.stud() );
@@ -137,8 +148,8 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
return FluentIterable.from( MPUserFileManager.get().getUsers() ).transform( new Function() {
@Nullable
@Override
- public ModelUser apply(final MPUser model) {
- return new ModelUser( model );
+ public ModelUser apply(@Nullable final MPUser model) {
+ return new ModelUser( Preconditions.checkNotNull( model ) );
}
} ).toArray( ModelUser.class );
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java
index 8523d485..d2009c6e 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelUser.java
@@ -48,7 +48,7 @@ public class ModelUser extends User {
public void authenticate(final char[] masterPassword)
throws IncorrectMasterPasswordException {
- model.authenticate( masterPassword );
+ putKey( model.authenticate( masterPassword ) );
this.masterPassword = masterPassword;
}
@@ -81,6 +81,7 @@ public class ModelUser extends User {
}
public boolean keySaved() {
+ // TODO
return false;
}
}
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
index be6f9314..c6fd071e 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java
@@ -149,11 +149,12 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} );
// Password
- passwordField = new JPasswordField();
- passwordField.setAlignmentX( Component.CENTER_ALIGNMENT );
- passwordField.setEditable( false );
- passwordField.setHorizontalAlignment( JTextField.CENTER );
- passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true );
+ passwordField = Components.passwordField();
+ passwordField.setAlignmentX(Component.CENTER_ALIGNMENT);
+ passwordField.setHorizontalAlignment(JTextField.CENTER);
+ passwordField.putClientProperty("JPasswordField.cutCopyAllowed", true);
+ passwordField.setEditable(false);
+ passwordField.setBackground(null);
passwordField.setBorder( null );
passwordEchoChar = passwordField.getEchoChar();
passwordEchoFont = passwordField.getFont().deriveFont( 40f );
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java
index 469c5a8f..5df63855 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/UnlockFrame.java
@@ -27,9 +27,21 @@ public class UnlockFrame extends JFrame {
super( "Unlock Master Password" );
this.signInCallback = signInCallback;
- setDefaultCloseOperation( DISPOSE_ON_CLOSE );
- setContentPane( root = Components.gradientPanel( new BorderLayout( 20, 20 ), Res.colors().frameBg() ) );
- root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
+ setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+ setContentPane(root = Components.gradientPanel(new BorderLayout(20, 20), Res.colors().frameBg()));
+ root.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ addWindowFocusListener(new WindowAdapter() {
+ @Override
+ public void windowGainedFocus(WindowEvent e) {
+ root.setGradientColor(Res.colors().frameBg());
+ }
+
+ @Override
+ public void windowLostFocus(WindowEvent e) {
+ root.setGradientColor(Color.RED);
+ }
+ });
authenticationContainer = Components.boxLayout( BoxLayout.PAGE_AXIS );
authenticationContainer.setOpaque( true );
@@ -91,10 +103,10 @@ public class UnlockFrame extends JFrame {
JComponent toolsPanel = Components.boxLayout( BoxLayout.LINE_AXIS, incognitoCheckBox, Box.createGlue() );
authenticationContainer.add( toolsPanel );
for (JButton button : authenticationPanel.getButtons()) {
- button.setMargin( new Insets( 0, 0, 0, 0 ) );
- button.setAlignmentX( RIGHT_ALIGNMENT );
- button.setBorder( null );
toolsPanel.add( button );
+ button.setMargin( new Insets( 0, 0, 0, 0 ) );
+ button.setAlignmentX(RIGHT_ALIGNMENT);
+ button.setContentAreaFilled(false);
}
checkSignIn();
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java
index 8dc73287..0f62e3d3 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/User.java
@@ -1,7 +1,6 @@
package com.lyndir.masterpassword.gui;
-import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
-
+import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.lyndir.masterpassword.MasterKey;
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
@@ -17,7 +16,7 @@ import javax.annotation.Nullable;
public abstract class User {
@Nonnull
- private static final EnumMap keyByVersion = Maps.newEnumMap( MasterKey.Version.class );
+ private final EnumMap keyByVersion = Maps.newEnumMap( MasterKey.Version.class );
public abstract String getFullName();
@@ -37,17 +36,23 @@ public abstract class User {
@Nonnull
public MasterKey getKey(MasterKey.Version algorithmVersion) {
- char[] masterPassword = getMasterPassword();
+ char[] masterPassword = Preconditions.checkNotNull( getMasterPassword(), "User is not authenticated: " + getFullName() );
MasterKey key = keyByVersion.get( algorithmVersion );
if (key == null)
- keyByVersion.put( algorithmVersion, key = MasterKey.create( algorithmVersion, getFullName(), masterPassword ) );
+ putKey( key = MasterKey.create( algorithmVersion, getFullName(), masterPassword ) );
if (!key.isValid())
key.revalidate( masterPassword );
return key;
}
+ protected void putKey(MasterKey masterKey) {
+ MasterKey oldKey = keyByVersion.put( masterKey.getAlgorithmVersion(), masterKey );
+ if (oldKey != null)
+ oldKey.invalidate();
+ }
+
public void reset() {
for (MasterKey key : keyByVersion.values())
key.invalidate();
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/AppleGUI.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java
similarity index 84%
rename from MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/AppleGUI.java
rename to MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java
index 34ae8a88..0e6559c6 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/AppleGUI.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java
@@ -1,6 +1,10 @@
-package com.lyndir.masterpassword.gui;
+package com.lyndir.masterpassword.gui.platform.mac;
import com.apple.eawt.*;
+import com.lyndir.masterpassword.gui.GUI;
+import com.lyndir.masterpassword.gui.PasswordFrame;
+import com.lyndir.masterpassword.gui.User;
+
import javax.swing.*;
diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/util/Components.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/util/Components.java
index 6b167437..1874349e 100644
--- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/util/Components.java
+++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/util/Components.java
@@ -70,10 +70,10 @@ public abstract class Components {
public static JPasswordField passwordField() {
return new JPasswordField() {
{
- setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
- BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
- setAlignmentX( LEFT_ALIGNMENT );
- setAlignmentY( BOTTOM_ALIGNMENT );
+ setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Res.colors().controlBorder(), 1, true),
+ BorderFactory.createEmptyBorder(4, 4, 4, 4)));
+ setAlignmentX(LEFT_ALIGNMENT);
+ setAlignmentY(BOTTOM_ALIGNMENT);
}
@Override
@@ -116,6 +116,7 @@ public abstract class Components {
((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder );
setAlignmentX( LEFT_ALIGNMENT );
setAlignmentY( BOTTOM_ALIGNMENT );
+ setBorder(null);
}
@Override
@@ -147,7 +148,8 @@ public abstract class Components {
public static JCheckBox checkBox(final String label) {
return new JCheckBox( label ) {
{
- setFont( Res.controlFont().deriveFont( 12f ) );
+ setFont(Res.controlFont().deriveFont(12f));
+ setBackground(null);
setAlignmentX( LEFT_ALIGNMENT );
setAlignmentY( BOTTOM_ALIGNMENT );
}
@@ -162,9 +164,14 @@ public abstract class Components {
public static JComboBox comboBox(final ComboBoxModel model) {
return new JComboBox( model ) {
{
+// CompoundBorder editorBorder = BorderFactory.createCompoundBorder(
+// BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
+// BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
+// ((JComponent) ((BasicComboBoxEditor) getEditor()).getEditorComponent()).setBorder(editorBorder);
setFont( Res.controlFont().deriveFont( 12f ) );
setAlignmentX( LEFT_ALIGNMENT );
setAlignmentY( BOTTOM_ALIGNMENT );
+// setBorder(null);
}
@Override
@@ -195,14 +202,17 @@ public abstract class Components {
public void setGradientColor(@Nullable final Color gradientColor) {
this.gradientColor = gradientColor;
+ revalidate();
}
@Override
public void doLayout() {
super.doLayout();
- if (gradientColor != null)
+ if (gradientColor != null) {
paint = new GradientPaint( new Point( 0, 0 ), gradientColor, new Point( getWidth(), getHeight() ), gradientColor.darker() );
+ repaint();
+ }
}
@Override
diff --git a/MasterPassword/Java/masterpassword-model/pom.xml b/MasterPassword/Java/masterpassword-model/pom.xml
index 395424a0..34ec8ae2 100644
--- a/MasterPassword/Java/masterpassword-model/pom.xml
+++ b/MasterPassword/Java/masterpassword-model/pom.xml
@@ -13,7 +13,6 @@
Master Password Site Model
A persistence model for Master Password sites.
- com.lyndir.masterpassword
masterpassword-model
jar
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
index 6e9d880a..71d8edb2 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSite.java
@@ -1,6 +1,5 @@
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.*;
@@ -56,7 +55,7 @@ public class MPSite {
return resultFor( masterKey, MPSiteVariant.Password, null );
}
- public String resultFor(final MasterKey masterKey, final MPSiteVariant variant, final String context) {
+ public String resultFor(final MasterKey masterKey, final MPSiteVariant variant, @Nullable final String context) {
return masterKey.encode( siteName, siteType, siteCounter, variant, context );
}
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java
index babfb40d..c6ff7740 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPSiteMarshaller.java
@@ -4,7 +4,6 @@ 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.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.MasterKey;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -51,11 +50,12 @@ public class MPSiteMarshaller {
}
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( contentMode.description() ).append( '\n' );
+ header.append( "# " ).append( this.contentMode.description() ).append( '\n' );
header.append( "# \n" );
header.append( "##\n" );
header.append( "# Format: 1\n" );
@@ -67,7 +67,7 @@ public class MPSiteMarshaller {
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( contentMode.name() ).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" );
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
index f90ca391..fac7d8a2 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUser.java
@@ -22,11 +22,11 @@ public class MPUser implements Comparable {
private final Collection sites = Sets.newHashSet();
@Nullable
- private byte[] keyID;
- private MasterKey.Version algorithmVersion;
- private int avatar;
- private MPSiteType defaultType;
- private ReadableInstant lastUsed;
+ private byte[] keyID;
+ private final MasterKey.Version algorithmVersion;
+ private int avatar;
+ private MPSiteType defaultType;
+ private ReadableInstant lastUsed;
public MPUser(final String fullName) {
this( fullName, null );
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java
index 7c0a1674..d3c5b749 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java
@@ -58,9 +58,9 @@ public class MPUserFileManager extends MPUserManager {
} ) ) ).transform( new Function() {
@Nullable
@Override
- public MPUser apply(final File file) {
+ public MPUser apply(@Nullable final File file) {
try {
- return MPSiteUnmarshaller.unmarshall( file ).getUser();
+ return MPSiteUnmarshaller.unmarshall( Preconditions.checkNotNull( file ) ).getUser();
}
catch (IOException e) {
logger.err( e, "Couldn't read user from: %s", file );
diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java
index 8c59763a..807d727c 100644
--- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java
+++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java
@@ -16,7 +16,7 @@ public abstract class MPUserManager {
return instance;
}
- public MPUserManager(final Iterable users) {
+ protected MPUserManager(final Iterable users) {
for (MPUser user : users)
addUser( user );
}
diff --git a/MasterPassword/ObjC/MPAlgorithm.h b/MasterPassword/ObjC/MPAlgorithm.h
index 958b9ba4..65a0cb43 100644
--- a/MasterPassword/ObjC/MPAlgorithm.h
+++ b/MasterPassword/ObjC/MPAlgorithm.h
@@ -48,9 +48,8 @@ NSString *NSStringFromTimeToCrack(TimeToCrack timeToCrack);
- (BOOL)tryMigrateUser:(MPUserEntity *)user inContext:(NSManagedObjectContext *)moc;
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit;
-- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName;
-- (MPKey *)keyFromKeyData:(NSData *)keyData;
- (NSData *)keyIDForKeyData:(NSData *)keyData;
+- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
- (NSString *)nameOfType:(MPSiteType)type;
- (NSString *)shortNameOfType:(MPSiteType)type;
diff --git a/MasterPassword/ObjC/MPAlgorithmV0.m b/MasterPassword/ObjC/MPAlgorithmV0.m
index 79f13dd0..bf5a7f46 100644
--- a/MasterPassword/ObjC/MPAlgorithmV0.m
+++ b/MasterPassword/ObjC/MPAlgorithmV0.m
@@ -89,7 +89,7 @@
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
- if (site.version != [self version] - 1)
+ if ([site.algorithm version] != [self version] - 1)
// Only migrate from previous version.
return NO;
@@ -101,24 +101,19 @@
// Apply migration.
site.requiresExplicitMigration = NO;
- site.version = [self version];
+ site.algorithm = self;
return YES;
}
-- (MPKey *)keyForPassword:(NSString *)password ofUserNamed:(NSString *)userName {
+- (NSData *)keyDataForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
NSDate *start = [NSDate date];
- uint8_t const *masterKeyBytes = mpw_masterKeyForUser( userName.UTF8String, password.UTF8String, [self version] );
- MPKey *masterKey = [self keyFromKeyData:[NSData dataWithBytes:masterKeyBytes length:MP_dkLen]];
+ uint8_t const *masterKeyBytes = mpw_masterKeyForUser( fullName.UTF8String, masterPassword.UTF8String, [self version] );
+ NSData *keyData = [NSData dataWithBytes:masterKeyBytes length:MP_dkLen];
+ trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", //
+ fullName, masterPassword, [self keyIDForKeyData:keyData], -[start timeIntervalSinceNow] );
mpw_free( masterKeyBytes, MP_dkLen );
- trc( @"User: %@, password: %@ derives to key ID: %@ (took %0.2fs)", userName, password, [masterKey.keyID encodeHex],
- -[start timeIntervalSinceNow] );
- return masterKey;
-}
-
-- (MPKey *)keyFromKeyData:(NSData *)keyData {
-
- return [[MPKey alloc] initWithKeyData:keyData algorithm:self];
+ return keyData;
}
- (NSData *)keyIDForKeyData:(NSData *)keyData {
@@ -322,8 +317,8 @@
- (NSString *)generateContentForSiteNamed:(NSString *)name ofType:(MPSiteType)type withCounter:(NSUInteger)counter
variant:(MPSiteVariant)variant context:(NSString *)context usingKey:(MPKey *)key {
- char const *contentBytes = mpw_passwordForSite( key.keyData.bytes, name.UTF8String, type, (uint32_t)counter,
- variant, context.UTF8String, [self version] );
+ char const *contentBytes = mpw_passwordForSite( [key keyDataForAlgorithm:self].bytes,
+ name.UTF8String, type, (uint32_t)counter, variant, context.UTF8String, [self version] );
NSString *content = [NSString stringWithCString:contentBytes encoding:NSUTF8StringEncoding];
mpw_freeString( contentBytes );
@@ -342,7 +337,7 @@
- (BOOL)savePassword:(NSString *)clearContent toSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
- NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (site.type) {
case MPSiteTypeGeneratedMaximum:
case MPSiteTypeGeneratedLong:
@@ -363,8 +358,9 @@
return NO;
}
+ NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
- encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
+ encryptWithSymmetricKey:encryptionKey padding:YES];
if ([((MPStoredSiteEntity *)site).contentObject isEqualToData:encryptedContent])
return NO;
@@ -378,8 +374,9 @@
return NO;
}
+ NSData *encryptionKey = [siteKey keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
NSData *encryptedContent = [[clearContent dataUsingEncoding:NSUTF8StringEncoding]
- encryptWithSymmetricKey:[siteKey subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
+ encryptWithSymmetricKey:encryptionKey padding:YES];
NSDictionary *siteQuery = [self queryForDevicePrivateSiteNamed:site.name];
if (!encryptedContent)
[PearlKeyChain deleteItemForQuery:siteQuery];
@@ -456,14 +453,14 @@
- (void)resolveLoginForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
- NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
NSString *name = site.name;
BOOL loginGenerated = site.loginGenerated && [[MPAppDelegate_Shared get] isFeatureUnlocked:MPProductGenerateLogins];
NSString *loginName = loginGenerated? nil: site.loginName;
id algorithm = nil;
if (!name.length)
err( @"Missing name." );
- else if (!siteKey.keyData.length)
+ else if (!siteKey)
err( @"Missing key." );
else
algorithm = site.algorithm;
@@ -478,7 +475,7 @@
- (void)resolvePasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
- NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (site.type) {
case MPSiteTypeGeneratedMaximum:
case MPSiteTypeGeneratedLong:
@@ -500,7 +497,7 @@
id algorithm = nil;
if (!site.name.length)
err( @"Missing name." );
- else if (!siteKey.keyData.length)
+ else if (!siteKey)
err( @"Missing key." );
else
algorithm = site.algorithm;
@@ -546,12 +543,12 @@
- (void)resolveAnswerForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey result:(void ( ^ )(NSString *result))resultBlock {
- NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
NSString *name = site.name;
id algorithm = nil;
if (!site.name.length)
err( @"Missing name." );
- else if (!siteKey.keyData.length)
+ else if (!siteKey)
err( @"Missing key." );
else
algorithm = site.algorithm;
@@ -565,13 +562,13 @@
- (void)resolveAnswerForQuestion:(MPSiteQuestionEntity *)question usingKey:(MPKey *)siteKey
result:(void ( ^ )(NSString *result))resultBlock {
- NSAssert( [siteKey.keyID isEqualToData:question.site.user.keyID], @"Site does not belong to current user." );
+ NSAssert( [[siteKey keyIDForAlgorithm:question.site.user.algorithm] isEqualToData:question.site.user.keyID], @"Site does not belong to current user." );
NSString *name = question.site.name;
NSString *keyword = question.keyword;
id algorithm = nil;
if (!name.length)
err( @"Missing name." );
- else if (!siteKey.keyData.length)
+ else if (!siteKey)
err( @"Missing key." );
else
algorithm = question.site.algorithm;
@@ -585,7 +582,7 @@
- (void)importProtectedPassword:(NSString *)protectedContent protectedByKey:(MPKey *)importKey
intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
- NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (site.type) {
case MPSiteTypeGeneratedMaximum:
case MPSiteTypeGeneratedLong:
@@ -603,7 +600,7 @@
(long)site.type, [site class] );
break;
}
- if ([importKey.keyID isEqualToData:siteKey.keyID])
+ if ([[importKey keyIDForAlgorithm:self] isEqualToData:[siteKey keyIDForAlgorithm:self]])
((MPStoredSiteEntity *)site).contentObject = [protectedContent decodeBase64];
else {
@@ -620,7 +617,7 @@
- (void)importClearTextPassword:(NSString *)clearContent intoSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
- NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
switch (site.type) {
case MPSiteTypeGeneratedMaximum:
case MPSiteTypeGeneratedLong:
@@ -644,7 +641,7 @@
- (NSString *)exportPasswordForSite:(MPSiteEntity *)site usingKey:(MPKey *)siteKey {
- NSAssert( [siteKey.keyID isEqualToData:site.user.keyID], @"Site does not belong to current user." );
+ NSAssert( [[siteKey keyIDForAlgorithm:site.user.algorithm] isEqualToData:site.user.keyID], @"Site does not belong to current user." );
if (!(site.type & MPSiteFeatureExportContent))
return nil;
@@ -701,8 +698,10 @@
if (!key)
return nil;
NSData *decryptedContent = nil;
- if ([encryptedContent length])
- decryptedContent = [encryptedContent decryptWithSymmetricKey:[key subKeyOfLength:PearlCryptKeySize].keyData padding:YES];
+ if ([encryptedContent length]) {
+ NSData *encryptionKey = [key keyDataForAlgorithm:self trimmedLength:PearlCryptKeySize];
+ decryptedContent = [encryptedContent decryptWithSymmetricKey:encryptionKey padding:YES];
+ }
if (!decryptedContent)
return nil;
@@ -711,7 +710,7 @@
- (BOOL)timeToCrack:(out TimeToCrack *)timeToCrack passwordOfType:(MPSiteType)type byAttacker:(MPAttacker)attacker {
- if (!type)
+ if (!(type & MPSiteTypeClassGenerated))
return NO;
size_t count = 0;
const char **templates = mpw_templatesForType( type, &count );
@@ -730,6 +729,7 @@
BN_add( permutations, permutations, templatePermutations );
}
BN_free( templatePermutations );
+ free( templates );
return [self timeToCrack:timeToCrack permutations:permutations forAttacker:attacker];
}
diff --git a/MasterPassword/ObjC/MPAlgorithmV1.m b/MasterPassword/ObjC/MPAlgorithmV1.m
index 44be3caa..a174de06 100644
--- a/MasterPassword/ObjC/MPAlgorithmV1.m
+++ b/MasterPassword/ObjC/MPAlgorithmV1.m
@@ -27,7 +27,7 @@
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
- if (site.version != [self version] - 1)
+ if ([site.algorithm version] != [self version] - 1)
// Only migrate from previous version.
return NO;
@@ -41,7 +41,7 @@
// Apply migration.
site.requiresExplicitMigration = NO;
- site.version = [self version];
+ site.algorithm = self;
return YES;
}
diff --git a/MasterPassword/ObjC/MPAlgorithmV2.m b/MasterPassword/ObjC/MPAlgorithmV2.m
index a68d24bf..b5e67f68 100644
--- a/MasterPassword/ObjC/MPAlgorithmV2.m
+++ b/MasterPassword/ObjC/MPAlgorithmV2.m
@@ -15,7 +15,6 @@
// Copyright 2012 lhunath (Maarten Billemont). All rights reserved.
//
-#import
#import "MPAlgorithmV2.h"
#import "MPEntities.h"
@@ -28,7 +27,7 @@
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
- if (site.version != [self version] - 1)
+ if ([site.algorithm version] != [self version] - 1)
// Only migrate from previous version.
return NO;
@@ -42,7 +41,7 @@
// Apply migration.
site.requiresExplicitMigration = NO;
- site.version = [self version];
+ site.algorithm = self;
return YES;
}
diff --git a/MasterPassword/ObjC/MPAlgorithmV3.m b/MasterPassword/ObjC/MPAlgorithmV3.m
index 0be028ee..4d4f8b19 100644
--- a/MasterPassword/ObjC/MPAlgorithmV3.m
+++ b/MasterPassword/ObjC/MPAlgorithmV3.m
@@ -27,12 +27,13 @@
- (BOOL)tryMigrateSite:(MPSiteEntity *)site explicit:(BOOL)explicit {
- if (site.version != [self version] - 1)
+ if ([site.algorithm version] != [self version] - 1)
// Only migrate from previous version.
return NO;
if (!explicit) {
- if (site.type & MPSiteTypeClassGenerated && site.name.length != [site.name dataUsingEncoding:NSUTF8StringEncoding].length) {
+ if (site.type & MPSiteTypeClassGenerated &&
+ site.user.name.length != [site.user.name dataUsingEncoding:NSUTF8StringEncoding].length) {
// This migration requires explicit permission for types of the generated class.
site.requiresExplicitMigration = YES;
return NO;
@@ -41,7 +42,7 @@
// Apply migration.
site.requiresExplicitMigration = NO;
- site.version = [self version];
+ site.algorithm = self;
return YES;
}
diff --git a/MasterPassword/ObjC/MPAppDelegate_Key.m b/MasterPassword/ObjC/MPAppDelegate_Key.m
index 0ba0fa90..2b993521 100644
--- a/MasterPassword/ObjC/MPAppDelegate_Key.m
+++ b/MasterPassword/ObjC/MPAppDelegate_Key.m
@@ -31,12 +31,12 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
NSData *keyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
if (!keyData) {
- inf( @"No key found in keychain for: %@", user.userID );
+ inf( @"No key found in keychain for user: %@", user.userID );
return nil;
}
- inf( @"Found key in keychain for: %@", user.userID );
- return [MPAlgorithmDefault keyFromKeyData:keyData];
+ inf( @"Found key in keychain for user: %@", user.userID );
+ return [[MPKey alloc] initForFullName:user.name withKeyData:keyData forAlgorithm:user.algorithm];
}
- (void)storeSavedKeyFor:(MPUserEntity *)user {
@@ -44,12 +44,12 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
if (user.saveKey) {
NSData *existingKeyData = [PearlKeyChain dataOfItemForQuery:keyQuery( user )];
- if (![existingKeyData isEqualToData:self.key.keyData]) {
- inf( @"Saving key in keychain for: %@", user.userID );
+ if (![existingKeyData isEqualToData:[self.key keyDataForAlgorithm:user.algorithm]]) {
+ inf( @"Saving key in keychain for user: %@", user.userID );
[PearlKeyChain addOrUpdateItemForQuery:keyQuery( user )
withAttributes:@{
- (__bridge id)kSecValueData : self.key.keyData,
+ (__bridge id)kSecValueData : [self.key keyDataForAlgorithm:user.algorithm],
#if TARGET_OS_IPHONE
(__bridge id)kSecAttrAccessible : (__bridge id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
#endif
@@ -62,7 +62,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
OSStatus result = [PearlKeyChain deleteItemForQuery:keyQuery( user )];
if (result == noErr) {
- inf( @"Removed key from keychain for: %@", user.userID );
+ inf( @"Removed key from keychain for user: %@", user.userID );
[[NSNotificationCenter defaultCenter] postNotificationName:MPKeyForgottenNotification object:self];
}
@@ -88,8 +88,8 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
// Method 1: When the user has no keyID set, set a new key from the given master password.
if (!user.keyID) {
- if ([password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name])) {
- user.keyID = tryKey.keyID;
+ if ([password length] && (tryKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:password])) {
+ user.keyID = [tryKey keyIDForAlgorithm:MPAlgorithmDefault];
// Migrate existing sites.
[self migrateSitesForUser:user saveInContext:moc toKey:tryKey];
@@ -103,9 +103,11 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
else if (!tryKey) {
// Key should be saved in keychain. Load it.
- if ((tryKey = [self loadSavedKeyFor:user]) && ![user.keyID isEqual:tryKey.keyID]) {
+ if ((tryKey = [self loadSavedKeyFor:user]) && ![user.keyID isEqual:[tryKey keyIDForAlgorithm:user.algorithm]]) {
// Loaded password doesn't match user's keyID. Forget saved password: it is incorrect.
- inf( @"Saved password doesn't match keyID for: %@", user.userID );
+ inf( @"Saved password doesn't match keyID for user: %@", user.userID );
+ trc( @"user keyID: %@ (version: %d) != authentication keyID: %@",
+ user.keyID, user.algorithm.version, [tryKey keyIDForAlgorithm:user.algorithm] );
tryKey = nil;
[self forgetSavedKeyFor:user];
@@ -113,9 +115,11 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
}
// Method 3: Check the given master password string.
- if (!tryKey && [password length] && (tryKey = [MPAlgorithmDefault keyForPassword:password ofUserNamed:user.name]) &&
- ![user.keyID isEqual:tryKey.keyID]) {
- inf( @"Key derived from password doesn't match keyID for: %@", user.userID );
+ if (!tryKey && [password length] && (tryKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:password]) &&
+ ![user.keyID isEqual:[tryKey keyIDForAlgorithm:user.algorithm]]) {
+ inf( @"Key derived from password doesn't match keyID for user: %@", user.userID );
+ trc( @"user keyID: %@ (version: %u) != authentication keyID: %@",
+ user.keyID, user.algorithm.version, [tryKey keyIDForAlgorithm:user.algorithm] );
tryKey = nil;
}
@@ -123,13 +127,22 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
// No more methods left, fail if key still not known.
if (!tryKey) {
if (password)
- inf( @"Login failed for: %@", user.userID );
+ inf( @"Password login failed for user: %@", user.userID );
+ else
+ dbg( @"Automatic login failed for user: %@", user.userID );
return NO;
}
- inf( @"Logged in: %@", user.userID );
+ inf( @"Logged in user: %@", user.userID );
if (![self.key isEqualToKey:tryKey]) {
+ // Upgrade the user's keyID if not at the default version yet.
+ if (user.algorithm.version != MPAlgorithmDefaultVersion) {
+ user.algorithm = MPAlgorithmDefault;
+ user.keyID = [tryKey keyIDForAlgorithm:user.algorithm];
+ inf( @"Upgraded keyID to version %u for user: %@", user.algorithm.version, user.userID );
+ }
+
self.key = tryKey;
[self storeSavedKeyFor:user];
}
@@ -205,7 +218,7 @@ static NSDictionary *keyQuery(MPUserEntity *user) {
// Don't Migrate
break;
- recoverKey = [site.algorithm keyForPassword:masterPassword ofUserNamed:user.name];
+ recoverKey = [[MPKey alloc] initForFullName:user.name withMasterPassword:masterPassword];
}
if (!content)
diff --git a/MasterPassword/ObjC/MPAppDelegate_Store.m b/MasterPassword/ObjC/MPAppDelegate_Store.m
index ca44a589..730f8622 100644
--- a/MasterPassword/ObjC/MPAppDelegate_Store.m
+++ b/MasterPassword/ObjC/MPAppDelegate_Store.m
@@ -423,14 +423,15 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
}
MPSiteType type = activeUser.defaultType;
- NSString *typeEntityName = [MPAlgorithmDefault classNameOfType:type];
+ id algorithm = MPAlgorithmDefault;
+ Class entityType = [algorithm classOfType:type];
- MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
+ MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
site.name = siteName;
site.user = activeUser;
site.type = type;
site.lastUsed = [NSDate date];
- site.version = MPAlgorithmDefaultVersion;
+ site.algorithm = algorithm;
NSError *error = nil;
if (site.objectID.isTemporaryID && ![context obtainPermanentIDsForObjects:@[ site ] error:&error])
@@ -454,14 +455,14 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
else {
// Type requires a different class of site. Recreate the site.
- NSString *typeEntityName = [site.algorithm classNameOfType:type];
- MPSiteEntity *newSite = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
+ Class entityType = [site.algorithm classOfType:type];
+ MPSiteEntity *newSite = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
newSite.type = type;
newSite.name = site.name;
newSite.user = site.user;
newSite.uses = site.uses;
newSite.lastUsed = site.lastUsed;
- newSite.version = site.version;
+ newSite.algorithm = site.algorithm;
newSite.loginName = site.loginName;
NSError *error = nil;
@@ -685,13 +686,13 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
inf( @"Import cancelled." );
return MPImportResultCancelled;
}
- MPKey *userKey = [MPAlgorithmDefault keyForPassword:userMasterPassword ofUserNamed:user? user.name: importUserName];
- if (user && ![userKey.keyID isEqualToData:user.keyID])
+ MPKey *userKey = [[MPKey alloc] initForFullName:user? user.name: importUserName withMasterPassword:userMasterPassword];
+ if (user && ![[userKey keyIDForAlgorithm:user.algorithm] isEqualToData:user.keyID])
return MPImportResultInvalidPassword;
__block MPKey *importKey = userKey;
- if (importKeyID && ![importKey.keyID isEqualToData:importKeyID])
- importKey = [importAlgorithm keyForPassword:askImportPassword( importUserName ) ofUserNamed:importUserName];
- if (importKeyID && ![importKey.keyID isEqualToData:importKeyID])
+ if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
+ importKey = [[MPKey alloc] initForFullName:importUserName withMasterPassword:askImportPassword( importUserName )];
+ if (importKeyID && ![[importKey keyIDForAlgorithm:importAlgorithm] isEqualToData:importKeyID])
return MPImportResultInvalidPassword;
// Delete existing sites.
@@ -710,7 +711,8 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
else {
user = [MPUserEntity insertNewObjectInContext:context];
user.name = importUserName;
- user.keyID = [userKey keyID];
+ user.algorithm = MPAlgorithmDefault;
+ user.keyID = [userKey keyIDForAlgorithm:user.algorithm];
if (importAvatar != NSNotFound)
user.avatar = importAvatar;
dbg( @"Created User: %@", [user debugDescription] );
@@ -728,19 +730,20 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
NSString *exportContent = siteElements[7];
// Create new site.
- NSString *typeEntityName = [MPAlgorithmForVersion( version ) classNameOfType:type];
- if (!typeEntityName) {
+ id algorithm = MPAlgorithmForVersion( version );
+ Class entityType = [algorithm classOfType:type];
+ if (!entityType) {
err( @"Invalid site type in import file: %@ has type %lu", siteName, (long)type );
return MPImportResultInternalError;
}
- MPSiteEntity *site = [NSEntityDescription insertNewObjectForEntityForName:typeEntityName inManagedObjectContext:context];
+ MPSiteEntity *site = (MPSiteEntity *)[entityType insertNewObjectInContext:context];
site.name = siteName;
site.loginName = loginName;
site.user = user;
site.type = type;
site.uses = uses;
site.lastUsed = lastUsed;
- site.version = version;
+ site.algorithm = algorithm;
if ([exportContent length]) {
if (clearText)
[site.algorithm importClearTextPassword:exportContent intoSite:site usingKey:userKey];
@@ -768,7 +771,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
- (NSString *)exportSitesRevealPasswords:(BOOL)revealPasswords {
MPUserEntity *activeUser = [self activeUserForMainThread];
- inf( @"Exporting sites, %@, for: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID );
+ inf( @"Exporting sites, %@, for user: %@", revealPasswords? @"revealing passwords": @"omitting passwords", activeUser.userID );
// Header.
NSMutableString *export = [NSMutableString new];
@@ -799,7 +802,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
NSDate *lastUsed = site.lastUsed;
NSUInteger uses = site.uses;
MPSiteType type = site.type;
- NSUInteger version = site.version;
+ id algorithm = site.algorithm;
NSUInteger counter = 0;
NSString *loginName = site.loginName;
NSString *siteName = site.name;
@@ -820,7 +823,7 @@ PearlAssociatedObjectProperty( NSNumber*, StoreCorrupted, storeCorrupted );
[export appendFormat:@"%@ %8ld %8s %25s\t%25s\t%@\n",
[[NSDateFormatter rfc3339DateFormatter] stringFromDate:lastUsed], (long)uses,
- [strf( @"%lu:%lu:%lu", (long)type, (long)version, (long)counter ) UTF8String],
+ [strf( @"%lu:%lu:%lu", (long)type, (long)[algorithm version], (long)counter ) UTF8String],
[(loginName?: @"") UTF8String], [siteName UTF8String], content?: @""];
}
diff --git a/MasterPassword/ObjC/MPEntities.h b/MasterPassword/ObjC/MPEntities.h
index 0d1930f3..db2a82c8 100644
--- a/MasterPassword/ObjC/MPEntities.h
+++ b/MasterPassword/ObjC/MPEntities.h
@@ -31,9 +31,8 @@
@property(readonly) NSString *typeClassName;
@property(readonly) Class typeClass;
@property(assign) NSUInteger uses;
-@property(assign) NSUInteger version;
@property(assign) BOOL requiresExplicitMigration;
-@property(readonly) id algorithm;
+@property(strong) id algorithm;
- (NSUInteger)use;
- (BOOL)tryMigrateExplicitly:(BOOL)explicit;
@@ -56,6 +55,7 @@
@property(assign) BOOL saveKey;
@property(assign) MPSiteType defaultType;
@property(readonly) NSString *userID;
+@property(strong) id algorithm;
+ (NSString *)idFor:(NSString *)userName;
diff --git a/MasterPassword/ObjC/MPEntities.m b/MasterPassword/ObjC/MPEntities.m
index b69d90a7..54dc7381 100644
--- a/MasterPassword/ObjC/MPEntities.m
+++ b/MasterPassword/ObjC/MPEntities.m
@@ -8,6 +8,7 @@
#import "MPEntities.h"
#import "MPAppDelegate_Shared.h"
+#import "MPAppDelegate_Key.h"
@implementation NSManagedObjectContext(MP)
@@ -90,14 +91,14 @@
self.uses_ = @(anUses);
}
-- (NSUInteger)version {
+- (id)algorithm {
- return [self.version_ unsignedIntegerValue];
+ return MPAlgorithmForVersion( MIN( MPAlgorithmVersionCurrent, MAX( MPAlgorithmVersion0, [self.version_ unsignedIntegerValue] ) ) );
}
-- (void)setVersion:(NSUInteger)version {
+- (void)setAlgorithm:(id)algorithm {
- self.version_ = @(version);
+ self.version_ = @([algorithm version]);
}
- (BOOL)requiresExplicitMigration {
@@ -110,11 +111,6 @@
self.requiresExplicitMigration_ = @(requiresExplicitMigration);
}
-- (id)algorithm {
-
- return MPAlgorithmForVersion( self.version );
-}
-
- (NSUInteger)use {
self.lastUsed = [NSDate date];
@@ -128,21 +124,31 @@
- (NSString *)debugDescription {
- @try {
- return strf( @"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
- NSStringFromClass( [self class] ), self.name, self.user.name, (long)self.type, (long)self.uses, self.lastUsed,
- (long)self.version,
- self.loginName, self.requiresExplicitMigration );
- } @catch (NSException *exception) {
- return strf( @"{%@: inaccessible: %@}",
- NSStringFromClass( [self class] ), [exception fullDescription] );
- }
+ __block NSString *debugDescription = strf( @"{%@: [recursing]}", [self class] );
+
+ static BOOL recursing = NO;
+ PearlIfNotRecursing( &recursing, ^{
+ @try {
+ debugDescription = strf(
+ @"{%@: name=%@, user=%@, type=%lu, uses=%ld, lastUsed=%@, version=%ld, loginName=%@, requiresExplicitMigration=%d}",
+ NSStringFromClass( [self class] ), self.name, self.user.name, (long)self.type, (long)self.uses, self.lastUsed,
+ (long)[self.algorithm version],
+ self.loginName, self.requiresExplicitMigration );
+ }
+ @catch (NSException *exception) {
+ debugDescription = strf( @"{%@: inaccessible: %@}",
+ NSStringFromClass( [self class] ), [exception fullDescription] );
+ }
+ } );
+
+ return debugDescription;
}
- (BOOL)tryMigrateExplicitly:(BOOL)explicit {
- while (self.version < MPAlgorithmDefaultVersion) {
- NSUInteger toVersion = self.version + 1;
+ MPAlgorithmVersion algorithmVersion;
+ while ((algorithmVersion = [self.algorithm version]) < MPAlgorithmDefaultVersion) {
+ NSUInteger toVersion = algorithmVersion + 1;
if (![MPAlgorithmForVersion( toVersion ) tryMigrateSite:self explicit:explicit]) {
wrn( @"%@ migration to version: %ld failed for site: %@",
explicit? @"Explicit": @"Automatic", (long)toVersion, self );
@@ -287,6 +293,17 @@
self.defaultType_ = @(aDefaultType);
}
+- (id)algorithm {
+
+ return MPAlgorithmForVersion( MIN( MPAlgorithmVersionCurrent, MAX( MPAlgorithmVersion0, [self.version_ unsignedIntegerValue] ) ) );
+}
+
+- (void)setAlgorithm:(id)version {
+
+ self.version_ = @([version version]);
+ [[MPAppDelegate_Shared get] forgetSavedKeyFor:self];
+}
+
- (NSString *)userID {
return [MPUserEntity idFor:self.name];
diff --git a/MasterPassword/ObjC/MPKey.h b/MasterPassword/ObjC/MPKey.h
index ac8b2854..084c5d17 100644
--- a/MasterPassword/ObjC/MPKey.h
+++ b/MasterPassword/ObjC/MPKey.h
@@ -16,17 +16,21 @@
//
#import
+#import "MPAlgorithm.h"
@protocol MPAlgorithm;
@interface MPKey : NSObject
-@property(nonatomic, readonly, strong) id algorithm;
-@property(nonatomic, readonly, strong) NSData *keyData;
-@property(nonatomic, readonly, strong) NSData *keyID;
+@property(nonatomic, readonly) NSString *fullName;
+
+- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword;
+- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData forAlgorithm:(id)algorithm;
+
+- (NSData *)keyIDForAlgorithm:(id)algorithm;
+- (NSData *)keyDataForAlgorithm:(id)algorithm;
+- (NSData *)keyDataForAlgorithm:(id)algorithm trimmedLength:(NSUInteger)subKeyLength;
-- (id)initWithKeyData:(NSData *)keyData algorithm:(id)algorithm;
-- (MPKey *)subKeyOfLength:(NSUInteger)subKeyLength;
- (BOOL)isEqualToKey:(MPKey *)key;
@end
diff --git a/MasterPassword/ObjC/MPKey.m b/MasterPassword/ObjC/MPKey.m
index 940d096d..cdef3611 100644
--- a/MasterPassword/ObjC/MPKey.m
+++ b/MasterPassword/ObjC/MPKey.m
@@ -1,12 +1,12 @@
/**
- * Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
- *
- * See the enclosed file LICENSE for license information (LGPLv3). If you did
- * not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
- *
- * @author Maarten Billemont
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt
- */
+* Copyright Maarten Billemont (http://www.lhunath.com, lhunath@lyndir.com)
+*
+* See the enclosed file LICENSE for license information (LGPLv3). If you did
+* not receive this file, see http://www.gnu.org/licenses/lgpl-3.0.txt
+*
+* @author Maarten Billemont
+* @license http://www.gnu.org/licenses/lgpl-3.0.txt
+*/
//
// MPKey
@@ -19,38 +19,61 @@
@interface MPKey()
-@property(nonatomic, readwrite, strong) id algorithm;
-@property(nonatomic, readwrite, strong) NSData *keyData;
-@property(nonatomic, readwrite, strong) NSData *keyID;
+@property(nonatomic) NSString *fullName;
+@property(nonatomic) NSString *masterPassword;
@end
-@implementation MPKey
+@implementation MPKey {
+ NSCache *_keyCache;
+};
-@synthesize algorithm = _algorithm, keyData = _keyData, keyID = _keyID;
-
-- (id)initWithKeyData:(NSData *)keyData algorithm:(id)algorithm {
+- (instancetype)initForFullName:(NSString *)fullName withMasterPassword:(NSString *)masterPassword {
if (!(self = [super init]))
return nil;
- self.keyData = keyData;
- self.algorithm = algorithm;
- self.keyID = [self.algorithm keyIDForKeyData:keyData];
+ _keyCache = [NSCache new];
+ self.fullName = fullName;
+ self.masterPassword = masterPassword;
return self;
}
-- (MPKey *)subKeyOfLength:(NSUInteger)subKeyLength {
+- (instancetype)initForFullName:(NSString *)fullName withKeyData:(NSData *)keyData forAlgorithm:(id)algorithm {
- NSData *subKeyData = [self.keyData subdataWithRange:NSMakeRange( 0, MIN(subKeyLength, self.keyData.length) )];
+ if (!(self = [self initForFullName:fullName withMasterPassword:nil]))
+ return nil;
- return [self.algorithm keyFromKeyData:subKeyData];
+ [_keyCache setObject:keyData forKey:algorithm];
+
+ return self;
+}
+
+- (NSData *)keyIDForAlgorithm:(id)algorithm {
+
+ return [algorithm keyIDForKeyData:[self keyDataForAlgorithm:algorithm]];
+}
+
+- (NSData *)keyDataForAlgorithm:(id)algorithm {
+
+ NSData *keyData = [_keyCache objectForKey:algorithm];
+ if (!keyData)
+ [_keyCache setObject:keyData = [algorithm keyDataForFullName:self.fullName withMasterPassword:self.masterPassword]
+ forKey:algorithm];
+
+ return keyData;
+}
+
+- (NSData *)keyDataForAlgorithm:(id)algorithm trimmedLength:(NSUInteger)subKeyLength {
+
+ NSData *keyData = [self keyDataForAlgorithm:algorithm];
+ return [keyData subdataWithRange:NSMakeRange( 0, MIN( subKeyLength, keyData.length ) )];
}
- (BOOL)isEqualToKey:(MPKey *)key {
- return [self.keyID isEqualToData:key.keyID];
+ return [self.fullName isEqualToString:key.fullName] && [self.masterPassword isEqualToString:self.masterPassword];
}
- (BOOL)isEqual:(id)object {
diff --git a/MasterPassword/ObjC/MPUserEntity.h b/MasterPassword/ObjC/MPUserEntity.h
index eb615712..ef838dd0 100644
--- a/MasterPassword/ObjC/MPUserEntity.h
+++ b/MasterPassword/ObjC/MPUserEntity.h
@@ -19,6 +19,7 @@
@property (nonatomic, retain) NSDate * lastUsed;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSNumber * saveKey_;
+@property (nonatomic, retain) NSNumber * version_;
@property (nonatomic, retain) NSSet *sites;
@end
diff --git a/MasterPassword/ObjC/MPUserEntity.m b/MasterPassword/ObjC/MPUserEntity.m
index da97fdcc..c2cdaff0 100644
--- a/MasterPassword/ObjC/MPUserEntity.m
+++ b/MasterPassword/ObjC/MPUserEntity.m
@@ -18,6 +18,7 @@
@dynamic lastUsed;
@dynamic name;
@dynamic saveKey_;
+@dynamic version_;
@dynamic sites;
@end
diff --git a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
index cefac5ed..ae733511 100644
--- a/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
+++ b/MasterPassword/ObjC/Mac/MPPasswordWindowController.m
@@ -605,6 +605,8 @@
CGDirectDisplayID displayID = [self.window.screen.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
CGImageRef capturedImage = CGDisplayCreateImage( displayID );
if (!capturedImage || CGImageGetWidth( capturedImage ) <= 1) {
+ if (capturedImage)
+ CFRelease( capturedImage );
wrn( @"Failed to capture screen image for display: %d", displayID );
return;
}
diff --git a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
index 51a87fa0..5b2d3466 100644
--- a/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
+++ b/MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj/project.pbxproj
@@ -31,7 +31,6 @@
DA16B345170661F2000A0EAB /* libPearl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC77CAD148291A600BCF976 /* libPearl.a */; };
DA2508F119511D3600AC23F1 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA2508F019511D3600AC23F1 /* MPPasswordWindowController.xib */; };
DA250925195148E200AC23F1 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAEBC45214F6364500987BF6 /* QuartzCore.framework */; };
- DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */; };
DA2CA4ED18D323D3007798F8 /* NSError+PearlFullDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */; };
DA2CA4EE18D323D3007798F8 /* NSError+PearlFullDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */; };
DA2CA4EF18D323D3007798F8 /* NSArray+Pearl.m in Sources */ = {isa = PBXBuildFile; fileRef = DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */; };
@@ -92,6 +91,7 @@
DA6774451A474A3B004F356A /* mpw-types.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C21A4746AF004F356A /* mpw-types.c */; };
DA6774461A474A3B004F356A /* mpw-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6773C51A4746AF004F356A /* mpw-util.c */; };
DA67744A1A47C8F7004F356A /* mpw-tests-util.c in Sources */ = {isa = PBXBuildFile; fileRef = DA6774481A47C8F7004F356A /* mpw-tests-util.c */; };
+ DA8495301A915EF400B3053D /* MasterPassword.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DA8495281A915EF400B3053D /* MasterPassword.xcdatamodeld */; };
DA89D4EC1A51EABD00AC64D7 /* Pearl-Cocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = DA89D4EA1A51EABD00AC64D7 /* Pearl-Cocoa.h */; };
DA89D4ED1A51EABD00AC64D7 /* Pearl-Cocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = DA89D4EB1A51EABD00AC64D7 /* Pearl-Cocoa.m */; };
DA8ED895192906920099B726 /* PearlTween.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8ED891192906920099B726 /* PearlTween.m */; };
@@ -294,11 +294,6 @@
DA25090719513C1400AC23F1 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
DA2509261951B86C00AC23F1 /* small-screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "small-screen.png"; sourceTree = ""; };
DA2509271951B86C00AC23F1 /* screen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screen.png; sourceTree = ""; };
- DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = ""; };
- DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; };
- DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; };
- DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = ""; };
- DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = ""; };
DA2CA4E718D323D3007798F8 /* NSError+PearlFullDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+PearlFullDescription.m"; sourceTree = ""; };
DA2CA4E818D323D3007798F8 /* NSError+PearlFullDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+PearlFullDescription.h"; sourceTree = ""; };
DA2CA4E918D323D3007798F8 /* NSArray+Pearl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+Pearl.m"; sourceTree = ""; };
@@ -321,7 +316,6 @@
DA32CFE119CF1C71004F3F0E /* MPSiteQuestionEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSiteQuestionEntity.m; sourceTree = ""; };
DA32CFE319CF1C71004F3F0E /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = ""; };
DA32CFE419CF1C71004F3F0E /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = ""; };
- DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = ""; };
DA3509FC15F101A500C14A8E /* PearlQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PearlQueue.h; sourceTree = ""; };
DA3509FD15F101A500C14A8E /* PearlQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlQueue.m; sourceTree = ""; };
DA3B844A190FC5A900246EEA /* Crashlytics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Crashlytics.framework; sourceTree = ""; };
@@ -796,6 +790,13 @@
DA831A281A6E1146000AC234 /* mpw-algorithm_v1.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v1.c"; sourceTree = ""; };
DA831A291A6E1146000AC234 /* mpw-algorithm_v2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v2.c"; sourceTree = ""; };
DA831A2A1A6E1146000AC234 /* mpw-algorithm_v3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "mpw-algorithm_v3.c"; sourceTree = ""; };
+ DA8495291A915EF400B3053D /* MasterPassword 1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 1.xcdatamodel"; sourceTree = ""; };
+ DA84952A1A915EF400B3053D /* MasterPassword 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 2.xcdatamodel"; sourceTree = ""; };
+ DA84952B1A915EF400B3053D /* MasterPassword 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 3.xcdatamodel"; sourceTree = ""; };
+ DA84952C1A915EF400B3053D /* MasterPassword 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 4.xcdatamodel"; sourceTree = ""; };
+ DA84952D1A915EF400B3053D /* MasterPassword 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 5.xcdatamodel"; sourceTree = ""; };
+ DA84952E1A915EF400B3053D /* MasterPassword 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 6.xcdatamodel"; sourceTree = ""; };
+ DA84952F1A915EF400B3053D /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = ""; };
DA89D4EA1A51EABD00AC64D7 /* Pearl-Cocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Pearl-Cocoa.h"; sourceTree = ""; };
DA89D4EB1A51EABD00AC64D7 /* Pearl-Cocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "Pearl-Cocoa.m"; sourceTree = ""; };
DA8ED891192906920099B726 /* PearlTween.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PearlTween.m; sourceTree = ""; };
@@ -1060,8 +1061,8 @@
DA5E5C961724A667003798D8 /* ObjC */ = {
isa = PBXGroup;
children = (
+ DA8495281A915EF400B3053D /* MasterPassword.xcdatamodeld */,
DA5E5CB21724A667003798D8 /* Mac */,
- DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */,
DA5E5C971724A667003798D8 /* MPAlgorithm.h */,
DA5E5C981724A667003798D8 /* MPAlgorithm.m */,
DA5E5C991724A667003798D8 /* MPAlgorithmV0.h */,
@@ -2245,7 +2246,6 @@
DA5180CE19FF307E00A587E9 /* MPAppDelegate_Store.m in Sources */,
DA5E5CFA1724A667003798D8 /* MPAppDelegate_Shared.m in Sources */,
DA5E5CFC1724A667003798D8 /* MPConfig.m in Sources */,
- DA29992C19C6A89900AF7DF1 /* MasterPassword.xcdatamodeld in Sources */,
DA3B8456190FC89700246EEA /* MPFixable.m in Sources */,
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */,
DA5E5D011724A667003798D8 /* MPKey.m in Sources */,
@@ -2262,6 +2262,7 @@
93D39784E725A34D1EE3FB3B /* MPInitialWindowController.m in Sources */,
DA32CFDF19CF1C70004F3F0E /* MPSiteEntity.m in Sources */,
93D394C4254EEB45FB335AFB /* MPSitesTableView.m in Sources */,
+ DA8495301A915EF400B3053D /* MasterPassword.xcdatamodeld in Sources */,
DA6774291A4746AF004F356A /* mpw-algorithm.c in Sources */,
93D395E4830290EBB6E71F34 /* MPNoStateButton.m in Sources */,
DA4DAE941A7D8117003E5423 /* MPAlgorithmV3.m in Sources */,
@@ -2943,17 +2944,18 @@
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
- DA29992619C6A89900AF7DF1 /* MasterPassword.xcdatamodeld */ = {
+ DA8495281A915EF400B3053D /* MasterPassword.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
- DA29992719C6A89900AF7DF1 /* MasterPassword 1.xcdatamodel */,
- DA29992819C6A89900AF7DF1 /* MasterPassword 2.xcdatamodel */,
- DA29992919C6A89900AF7DF1 /* MasterPassword 3.xcdatamodel */,
- DA29992A19C6A89900AF7DF1 /* MasterPassword 4.xcdatamodel */,
- DA29992B19C6A89900AF7DF1 /* MasterPassword 5.xcdatamodel */,
- DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */,
+ DA8495291A915EF400B3053D /* MasterPassword 1.xcdatamodel */,
+ DA84952A1A915EF400B3053D /* MasterPassword 2.xcdatamodel */,
+ DA84952B1A915EF400B3053D /* MasterPassword 3.xcdatamodel */,
+ DA84952C1A915EF400B3053D /* MasterPassword 4.xcdatamodel */,
+ DA84952D1A915EF400B3053D /* MasterPassword 5.xcdatamodel */,
+ DA84952E1A915EF400B3053D /* MasterPassword 6.xcdatamodel */,
+ DA84952F1A915EF400B3053D /* MasterPassword 7.xcdatamodel */,
);
- currentVersion = DA32D00019CF470E004F3F0E /* MasterPassword 6.xcdatamodel */;
+ currentVersion = DA84952F1A915EF400B3053D /* MasterPassword 7.xcdatamodel */;
path = MasterPassword.xcdatamodeld;
sourceTree = "";
versionGroupType = wrapper.xcdatamodel;
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/.xccurrentversion b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/.xccurrentversion
index 19de7217..1ac59ba4 100644
--- a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/.xccurrentversion
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/.xccurrentversion
@@ -3,6 +3,6 @@
_XCCurrentVersionName
- MasterPassword 6.xcdatamodel
+ MasterPassword 7.xcdatamodel
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 6.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 6.xcdatamodel/contents
index f305f0c2..7c5f7ca9 100644
--- a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 6.xcdatamodel/contents
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 6.xcdatamodel/contents
@@ -1,5 +1,5 @@
-
+
diff --git a/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 7.xcdatamodel/contents b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 7.xcdatamodel/contents
new file mode 100644
index 00000000..536886aa
--- /dev/null
+++ b/MasterPassword/ObjC/MasterPassword.xcdatamodeld/MasterPassword 7.xcdatamodel/contents
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MasterPassword/ObjC/iOS/MPEmergencyViewController.h b/MasterPassword/ObjC/iOS/MPEmergencyViewController.h
index 9eac4c9c..a974cc27 100644
--- a/MasterPassword/ObjC/iOS/MPEmergencyViewController.h
+++ b/MasterPassword/ObjC/iOS/MPEmergencyViewController.h
@@ -22,7 +22,7 @@
@property(weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property(weak, nonatomic) IBOutlet UIView *dialogView;
@property(weak, nonatomic) IBOutlet UIView *containerView;
-@property(weak, nonatomic) IBOutlet UITextField *userNameField;
+@property(weak, nonatomic) IBOutlet UITextField *fullNameField;
@property(weak, nonatomic) IBOutlet UITextField *masterPasswordField;
@property(weak, nonatomic) IBOutlet UITextField *siteField;
@property(weak, nonatomic) IBOutlet UIStepper *counterStepper;
diff --git a/MasterPassword/ObjC/iOS/MPEmergencyViewController.m b/MasterPassword/ObjC/iOS/MPEmergencyViewController.m
index b54718e5..99eeaa0d 100644
--- a/MasterPassword/ObjC/iOS/MPEmergencyViewController.m
+++ b/MasterPassword/ObjC/iOS/MPEmergencyViewController.m
@@ -75,7 +75,7 @@
- (IBAction)controlChanged:(UIControl *)control {
- if (control == self.userNameField || control == self.masterPasswordField)
+ if (control == self.fullNameField || control == self.masterPasswordField)
[self updateKey];
else
[self updatePassword];
@@ -103,15 +103,15 @@
- (void)updateKey {
- NSString *userName = self.userNameField.text;
+ NSString *fullName = self.fullNameField.text;
NSString *masterPassword = self.masterPasswordField.text;
self.passwordLabel.text = nil;
[self.activity startAnimating];
[_emergencyKeyQueue cancelAllOperations];
[_emergencyKeyQueue addOperationWithBlock:^{
- if ([masterPassword length] && [userName length])
- _key = [MPAlgorithmDefault keyForPassword:masterPassword ofUserNamed:userName];
+ if ([masterPassword length] && [fullName length])
+ _key = [[MPKey alloc] initForFullName:fullName withMasterPassword:masterPassword];
else
_key = nil;
@@ -165,7 +165,7 @@
- (void)reset {
- self.userNameField.text = nil;
+ self.fullNameField.text = nil;
self.masterPasswordField.text = nil;
self.siteField.text = nil;
self.counterStepper.value = 1;
diff --git a/MasterPassword/ObjC/iOS/MPMessageViewController.h b/MasterPassword/ObjC/iOS/MPMessageViewController.h
new file mode 100644
index 00000000..5fd1578c
--- /dev/null
+++ b/MasterPassword/ObjC/iOS/MPMessageViewController.h
@@ -0,0 +1,25 @@
+//
+// MPPreferencesViewController.h
+// MasterPassword-iOS
+//
+// Created by Maarten Billemont on 04/06/12.
+// Copyright (c) 2012 Lyndir. All rights reserved.
+//
+
+#import
+
+@interface MPMessage : NSObject
+
+@property(nonatomic) NSString *title;
+@property(nonatomic) NSString *text;
+@property(nonatomic) BOOL info;
+
++ (instancetype)messageWithTitle:(NSString *)title text:(NSString *)text info:(BOOL)info;
+
+@end
+
+@interface MPMessageViewController : UIViewController
+
+@property (nonatomic) MPMessage *message;
+
+@end
diff --git a/MasterPassword/ObjC/iOS/MPMessageViewController.m b/MasterPassword/ObjC/iOS/MPMessageViewController.m
new file mode 100644
index 00000000..b6c2e0d0
--- /dev/null
+++ b/MasterPassword/ObjC/iOS/MPMessageViewController.m
@@ -0,0 +1,78 @@
+//
+// MPPreferencesViewController.m
+// MasterPassword-iOS
+//
+// Created by Maarten Billemont on 04/06/12.
+// Copyright (c) 2012 Lyndir. All rights reserved.
+//
+
+#import "MPMessageViewController.h"
+#import "MPiOSAppDelegate.h"
+#import "MPAppDelegate_Store.h"
+#import "MPOverlayViewController.h"
+
+@interface MPMessageViewController()
+
+@property(nonatomic) IBOutlet UILabel *titleLabel;
+@property(nonatomic) IBOutlet UILabel *messageLabel;
+@property(nonatomic) IBOutlet UIView *infoView;
+
+@end
+
+@implementation MPMessage
+
++ (instancetype)messageWithTitle:(NSString *)title text:(NSString *)text info:(BOOL)info {
+
+ MPMessage *message = [MPMessage new];
+ message.title = title;
+ message.text = text;
+ message.info = info;
+
+ return message;
+}
+
+@end
+
+@implementation MPMessageViewController
+
+#pragma mark - Life
+
+- (void)viewDidLoad {
+
+ [super viewDidLoad];
+
+ self.view.backgroundColor = [UIColor clearColor];
+}
+
+- (void)viewWillAppear:(BOOL)animated {
+
+ [super viewWillAppear:animated];
+
+ self.titleLabel.text = self.message.title;
+ self.messageLabel.text = self.message.text;
+ self.infoView.gone = !self.message.info;
+
+ PearlAddNotificationObserver( MPSignedOutNotification, nil, [NSOperationQueue mainQueue],
+ ^(MPMessageViewController *self, NSNotification *note) {
+ if (![note.userInfo[@"animated"] boolValue])
+ [UIView setAnimationsEnabled:NO];
+ [[MPOverlaySegue dismissViewController:self] perform];
+ [UIView setAnimationsEnabled:YES];
+ } );
+}
+
+- (void)viewWillDisappear:(BOOL)animated {
+
+ [super viewWillDisappear:animated];
+
+ PearlRemoveNotificationObservers();
+}
+
+- (UIStatusBarStyle)preferredStatusBarStyle {
+
+ return UIStatusBarStyleLightContent;
+}
+
+#pragma mark - State
+
+@end
diff --git a/MasterPassword/ObjC/iOS/MPPasswordCell.m b/MasterPassword/ObjC/iOS/MPPasswordCell.m
index 5fe6ec36..7261aa89 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordCell.m
+++ b/MasterPassword/ObjC/iOS/MPPasswordCell.m
@@ -59,6 +59,8 @@
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doRevealPassword: )]];
[self.counterButton addGestureRecognizer:
[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doResetCounter: )]];
+ [self.upgradeButton addGestureRecognizer:
+ [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector( doDowngrade: )]];
[self setupLayer];
@@ -331,13 +333,36 @@
- (IBAction)doUpgrade:(UIButton *)sender {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
- if (![[self siteInContext:context] tryMigrateExplicitly:YES]) {
+ MPSiteEntity *siteEntity = [self siteInContext:context];
+ if (![siteEntity tryMigrateExplicitly:YES]) {
[PearlOverlay showTemporaryOverlayWithTitle:@"Couldn't Upgrade Site" dismissAfter:2];
return;
}
[context saveToStore];
- [PearlOverlay showTemporaryOverlayWithTitle:@"Site Upgraded" dismissAfter:2];
+ [PearlOverlay showTemporaryOverlayWithTitle:strf( @"Site Upgraded to V%d", siteEntity.algorithm.version )
+ dismissAfter:2];
+ [self updateAnimated:YES];
+ }];
+}
+
+- (IBAction)doDowngrade:(UILongPressGestureRecognizer *)recognizer {
+
+ if (recognizer.state != UIGestureRecognizerStateBegan)
+ return;
+
+ if (![[MPiOSConfig get].allowDowngrade boolValue])
+ return;
+
+ [MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
+ MPSiteEntity *siteEntity = [self siteInContext:context];
+ if (siteEntity.algorithm.version <= 0)
+ return;
+
+ siteEntity.algorithm = MPAlgorithmForVersion( siteEntity.algorithm.version - 1 );
+ [context saveToStore];
+ [PearlOverlay showTemporaryOverlayWithTitle:strf( @"Site Downgraded to V%d", siteEntity.algorithm.version )
+ dismissAfter:2];
[self updateAnimated:YES];
}];
}
@@ -479,7 +504,7 @@
MPSiteEntity *mainSite = [self siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
// UI
- self.upgradeButton.gone = !mainSite.requiresExplicitMigration;
+ self.upgradeButton.gone = !mainSite.requiresExplicitMigration && ![[MPiOSConfig get].allowDowngrade boolValue];
self.answersButton.gone = ![[MPiOSAppDelegate get] isFeatureUnlocked:MPProductGenerateAnswers];
BOOL settingsMode = self.mode == MPPasswordCellModeSettings;
self.loginNameContainer.alpha = settingsMode || mainSite.loginGenerated || [mainSite.loginName length]? 0.7f: 0;
@@ -533,15 +558,15 @@
[algorithm timeToCrack:&timeToCrack passwordString:password byAttacker:attackHardware])
timeToCrackString = NSStringFromTimeToCrack( timeToCrack );
+ BOOL requiresExplicitMigration = site.requiresExplicitMigration;
+
PearlMainQueue( ^{
self.loginNameField.text = loginName;
self.passwordField.text = password;
self.strengthLabel.text = timeToCrackString;
self.loginNameButton.titleLabel.alpha = [loginName length] || self.loginNameField.enabled? 0: 1;
- if ([password length])
- self.indicatorView.alpha = 0;
- else {
+ if (![password length]) {
self.indicatorView.alpha = 1;
[self.indicatorView removeFromSuperview];
[self.modeScrollView addSubview:self.indicatorView];
@@ -551,6 +576,18 @@
@"target" : settingsMode? self.editButton: self.modeButton
}];
}
+ else if (requiresExplicitMigration) {
+ self.indicatorView.alpha = 1;
+ [self.indicatorView removeFromSuperview];
+ [self.modeScrollView addSubview:self.indicatorView];
+ [self.contentView addConstraintsWithVisualFormat:@"V:[indicator][target]" options:NSLayoutFormatAlignAllCenterX
+ metrics:nil views:@{
+ @"indicator" : self.indicatorView,
+ @"target" : settingsMode? self.upgradeButton: self.modeButton
+ }];
+ }
+ else
+ self.indicatorView.alpha = 0;
} );
}];
diff --git a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m
index e0aa4753..3a75b112 100644
--- a/MasterPassword/ObjC/iOS/MPPasswordsViewController.m
+++ b/MasterPassword/ObjC/iOS/MPPasswordsViewController.m
@@ -23,6 +23,7 @@
#import "MPAppDelegate_Key.h"
#import "MPPasswordCell.h"
#import "MPAnswersViewController.h"
+#import "MPMessageViewController.h"
typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
MPPasswordsBadNameTip = 1 << 0,
@@ -90,7 +91,13 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
[MPiOSAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *context) {
MPUserEntity *activeUser = [[MPiOSAppDelegate get] activeUserInContext:context];
if (![MPAlgorithmDefault tryMigrateUser:activeUser inContext:context])
- [PearlOverlay showTemporaryOverlayWithTitle:@"Some Sites Need Upgrade" dismissAfter:2];
+ [self performSegueWithIdentifier:@"message" sender:
+ [MPMessage messageWithTitle:@"You have sites that can be upgraded." text:
+ @"Upgrading a site allows it to take advantage of the latest improvements in the Master Password algorithm.\n\n"
+ "When you upgrade a site, a new and stronger password will be generated for it. To upgrade a site, first log into the site, navigate to your account preferences where you can change the site's password. Make sure you fill in any \"current password\" fields on the website first, then press the upgrade button here to get your new site password.\n\n"
+ "You can then update your site's account with the new and stronger password.\n\n"
+ "The upgrade button can be found in the site's settings and looks like this:"
+ info:YES]];
[context saveToStore];
}];
}
@@ -109,6 +116,8 @@ typedef NS_OPTIONS( NSUInteger, MPPasswordsTips ) {
if ([segue.identifier isEqualToString:@"answers"])
((MPAnswersViewController *)segue.destinationViewController).site =
[[MPPasswordCell findAsSuperviewOf:sender] siteInContext:[MPiOSAppDelegate managedObjectContextForMainThreadIfReady]];
+ if ([segue.identifier isEqualToString:@"message"])
+ ((MPMessageViewController *)segue.destinationViewController).message = sender;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
diff --git a/MasterPassword/ObjC/iOS/MPUsersViewController.m b/MasterPassword/ObjC/iOS/MPUsersViewController.m
index f25869bc..f5a21501 100644
--- a/MasterPassword/ObjC/iOS/MPUsersViewController.m
+++ b/MasterPassword/ObjC/iOS/MPUsersViewController.m
@@ -214,6 +214,7 @@ typedef NS_ENUM( NSUInteger, MPActiveUserState ) {
MPUserEntity *user = [self userForAvatar:avatarCell inContext:context isNew:&isNew];
if (isNew) {
user = [MPUserEntity insertNewObjectInContext:context];
+ user.algorithm = MPAlgorithmDefault;
user.avatar = avatarCell.avatar;
user.name = avatarCell.name;
}
diff --git a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m
index 7b751758..eea75a52 100644
--- a/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m
+++ b/MasterPassword/ObjC/iOS/MPiOSAppDelegate.m
@@ -73,9 +73,9 @@
PearlAddNotificationObserver( MPCheckConfigNotification, nil, [NSOperationQueue mainQueue], ^(id self, NSNotification *note) {
[self updateConfigKey:note.object];
} );
- PearlAddNotificationObserver( kIASKAppSettingChanged, nil, nil, ^(id self, NSNotification *note) {
- [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object];
- } );
+// PearlAddNotificationObserver( kIASKAppSettingChanged, nil, nil, ^(id self, NSNotification *note) {
+// [[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:note.object];
+// } );
PearlAddNotificationObserver( NSUserDefaultsDidChangeNotification, nil, nil, ^(id self, NSNotification *note) {
[[NSNotificationCenter defaultCenter] postNotificationName:MPCheckConfigNotification object:nil];
} );
@@ -496,7 +496,7 @@
return;
[moc performBlockAndWait:^{
- inf( @"Unsetting master password for: %@.", user.userID );
+ inf( @"Clearing keyID for user: %@.", user.userID );
user.keyID = nil;
[self forgetSavedKeyFor:user];
[moc saveToStore];
diff --git a/MasterPassword/ObjC/iOS/MPiOSConfig.h b/MasterPassword/ObjC/iOS/MPiOSConfig.h
index 4061ce56..0c99d306 100644
--- a/MasterPassword/ObjC/iOS/MPiOSConfig.h
+++ b/MasterPassword/ObjC/iOS/MPiOSConfig.h
@@ -18,6 +18,7 @@
@property(nonatomic, retain) NSNumber *loginNameTipShown;
@property(nonatomic, retain) NSNumber *traceMode;
@property(nonatomic, retain) NSNumber *dictationSearch;
+@property(nonatomic, retain) NSNumber *allowDowngrade;
@property(nonatomic, retain) NSNumber *developmentFuelRemaining;
@property(nonatomic, retain) NSNumber *developmentFuelInvested;
@property(nonatomic, retain) NSNumber *developmentFuelConsumption;
diff --git a/MasterPassword/ObjC/iOS/MPiOSConfig.m b/MasterPassword/ObjC/iOS/MPiOSConfig.m
index b6806958..4baf43d6 100644
--- a/MasterPassword/ObjC/iOS/MPiOSConfig.m
+++ b/MasterPassword/ObjC/iOS/MPiOSConfig.m
@@ -8,7 +8,7 @@
@implementation MPiOSConfig
-@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch;
+@dynamic helpHidden, siteInfoHidden, showSetup, actionsTipShown, typeTipShown, loginNameTipShown, traceMode, dictationSearch, allowDowngrade;
@dynamic developmentFuelRemaining, developmentFuelInvested, developmentFuelConsumption, developmentFuelChecked;
- (id)init {
@@ -26,6 +26,7 @@
NSStringFromSelector( @selector( loginNameTipShown ) ) : @NO,
NSStringFromSelector( @selector( traceMode ) ) : @NO,
NSStringFromSelector( @selector( dictationSearch ) ) : @NO,
+ NSStringFromSelector( @selector( allowDowngrade ) ) : @NO,
}];
return self;
diff --git a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj
index 01fc85a2..dfb53cfd 100644
--- a/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj
+++ b/MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj/project.pbxproj
@@ -22,6 +22,7 @@
93D393DB5325820241BA90A7 /* PearlSizedTextView.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D39A4759186F6D2D34AA6B /* PearlSizedTextView.h */; };
93D394982CBD25D46692DD7C /* MPWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */; };
93D394B5036C882B33C71872 /* MPPasswordsSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */; };
+ 93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399B36CDB2004D7C51391 /* MPMessageViewController.m */; };
93D39536EB550E811CCD04BC /* UIResponder+PearlFirstResponder.h in Headers */ = {isa = PBXBuildFile; fileRef = 93D394482BB07F90E8FD1314 /* UIResponder+PearlFirstResponder.h */; };
93D3954E96236384AFA00453 /* UIScrollView+PearlAdjustInsets.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D390FB3110DCCE68E600DC /* UIScrollView+PearlAdjustInsets.m */; };
93D3954FCE045A3CC7E804B7 /* MPUsersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */; };
@@ -179,6 +180,8 @@
DA7304E5194E025900E72520 /* tip_basic_black.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38901711E29700CF925C /* tip_basic_black.png */; };
DA7304E6194E025900E72520 /* tip_basic_black@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD38911711E29700CF925C /* tip_basic_black@2x.png */; };
DA7304E7194E027C00E72520 /* Square-bottom.png in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5C3C1723681B003798D8 /* Square-bottom.png */; };
+ DA8495311A93049300B3053D /* icon_down.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD375C1711E29500CF925C /* icon_down.png */; };
+ DA8495321A93049300B3053D /* icon_down@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DABD375D1711E29500CF925C /* icon_down@2x.png */; };
DA854C8318D4CFBF00106317 /* avatar-add@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = DA854C8118D4CFBF00106317 /* avatar-add@2x.png */; };
DA854C8418D4CFBF00106317 /* avatar-add.png in Resources */ = {isa = PBXBuildFile; fileRef = DA854C8218D4CFBF00106317 /* avatar-add.png */; };
DA945C8717E3F3FD0053236B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DA945C8617E3F3FD0053236B /* Images.xcassets */; };
@@ -515,6 +518,7 @@
93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordCell.h; sourceTree = ""; };
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCoachmarkViewController.h; sourceTree = ""; };
93D399A8E3181B442D347CD7 /* MPAlgorithmV2.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAlgorithmV2.m; sourceTree = ""; };
+ 93D399B36CDB2004D7C51391 /* MPMessageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMessageViewController.m; sourceTree = ""; };
93D399C2F3D48E57C4803BDC /* NSPersistentStore+PearlMigration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSPersistentStore+PearlMigration.m"; sourceTree = ""; };
93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUsersViewController.m; sourceTree = ""; };
93D399F244BB522A317811BB /* MPFixable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFixable.h; sourceTree = ""; };
@@ -540,6 +544,7 @@
93D39C426E03358384018E85 /* MPAnswersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAnswersViewController.m; sourceTree = ""; };
93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordsSegue.h; sourceTree = ""; };
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppSettingsViewController.h; sourceTree = ""; };
+ 93D39CB0EABD2748740992D8 /* MPMessageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMessageViewController.h; sourceTree = ""; };
93D39CC01630D0421205C4C4 /* MPNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNavigationController.m; sourceTree = ""; };
93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPEmergencyViewController.h; sourceTree = ""; };
93D39CECA10BCCB0BA581BF1 /* MPAppDelegate_InApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate_InApp.h; sourceTree = ""; };
@@ -669,6 +674,7 @@
DA70EC7F1811B13C00F65DB2 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
DA72BD7419C133BF00E6ACFE /* libscryptenc-ios-sim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libscryptenc-ios-sim.a"; sourceTree = ""; };
DA72BD7719C137D500E6ACFE /* libopensslcrypto-ios-dev.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libopensslcrypto-ios-dev.a"; sourceTree = ""; };
+ DA8495271A9146E600B3053D /* MasterPassword 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MasterPassword 7.xcdatamodel"; sourceTree = ""; };
DA854C8118D4CFBF00106317 /* avatar-add@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add@2x.png"; sourceTree = ""; };
DA854C8218D4CFBF00106317 /* avatar-add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "avatar-add.png"; sourceTree = ""; };
DA945C8617E3F3FD0053236B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
@@ -1745,6 +1751,8 @@
93D3924D6F77E6BF41AC32D3 /* MPRootSegue.h */,
93D39C41A27AA42D044D68AE /* NSString+MPMarkDown.m */,
93D393CB0B1F4EC8C17CFE43 /* NSString+MPMarkDown.h */,
+ 93D399B36CDB2004D7C51391 /* MPMessageViewController.m */,
+ 93D39CB0EABD2748740992D8 /* MPMessageViewController.h */,
);
sourceTree = "";
};
@@ -2606,55 +2614,55 @@
DABD3BD71711E2DC00CF925C /* iOS */ = {
isa = PBXGroup;
children = (
- DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */,
- 93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */,
- 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */,
- 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */,
- 93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */,
- DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */,
- DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */,
- DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */,
- DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */,
- DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */,
- DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */,
- DABD3BEC1711E2DC00CF925C /* MPTypeViewController.h */,
- DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */,
- DABD3BF01711E2DC00CF925C /* MPiOSConfig.h */,
- DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */,
+ DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */,
+ DABD3BFC1711E2DC00CF925C /* main.m */,
DABD3BF31711E2DC00CF925C /* MasterPassword-Info.plist */,
DABD3BF41711E2DC00CF925C /* MasterPassword-Prefix.pch */,
DABD3BF81711E2DC00CF925C /* MasterPassword.entitlements */,
- DABD3BF91711E2DC00CF925C /* Settings.bundle */,
- DABD3BFA1711E2DC00CF925C /* InfoPlist.strings */,
- DABD3BFC1711E2DC00CF925C /* main.m */,
- 93D39A28369954D147E239BA /* MPSetupViewController.m */,
- 93D39730673227EFF6DEFF19 /* MPSetupViewController.h */,
- 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */,
- 93D391943675426839501BB8 /* MPLogsViewController.h */,
- 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */,
- 93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */,
- 93D393310223DDB35218467A /* MPCombinedViewController.m */,
- 93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */,
- 93D39B381350802A194BF332 /* MPAvatarCell.m */,
- 93D39DA27D768B53C8B1330C /* MPAvatarCell.h */,
- 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */,
- 93D3971FE104BB4052484151 /* MPUsersViewController.h */,
- 93D39BAA71DE51B4D8A1286C /* MPCell.m */,
- 93D390519405B76CC6A57C4F /* MPCell.h */,
- 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */,
- 93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */,
- 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
- 93D392876BE5C011DE73B43F /* MPPopdownSegue.h */,
- 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */,
93D39C86E984EC65DA5ACB1D /* MPAppSettingsViewController.h */,
- 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */,
+ 93D3916C1D8F1427DFBDEBCA /* MPAppSettingsViewController.m */,
+ 93D39DA27D768B53C8B1330C /* MPAvatarCell.h */,
+ 93D39B381350802A194BF332 /* MPAvatarCell.m */,
+ 93D390519405B76CC6A57C4F /* MPCell.h */,
+ 93D39BAA71DE51B4D8A1286C /* MPCell.m */,
93D3999693660C89A7465F4E /* MPCoachmarkViewController.h */,
- 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */,
- 93D39F556F2F142740A65E59 /* MPWebViewController.h */,
- 93D39CC01630D0421205C4C4 /* MPNavigationController.m */,
+ 93D3995B1D4DCE5A30D882BA /* MPCoachmarkViewController.m */,
+ 93D39CF8ADF4542CDC4CD385 /* MPCombinedViewController.h */,
+ 93D393310223DDB35218467A /* MPCombinedViewController.m */,
+ 93D39CDD434AFD6E1B0DA359 /* MPEmergencyViewController.h */,
+ 93D39ACBA9F4878B6A1CC33B /* MPEmergencyViewController.m */,
+ DABD3BE61711E2DC00CF925C /* MPGuideViewController.h */,
+ DABD3BE71711E2DC00CF925C /* MPGuideViewController.m */,
+ DABD3BD81711E2DC00CF925C /* MPiOSAppDelegate.h */,
+ DABD3BD91711E2DC00CF925C /* MPiOSAppDelegate.m */,
+ DABD3BF01711E2DC00CF925C /* MPiOSConfig.h */,
+ DABD3BF11711E2DC00CF925C /* MPiOSConfig.m */,
+ 93D391943675426839501BB8 /* MPLogsViewController.h */,
+ 93D3979190DACEBD1F6AE9F4 /* MPLogsViewController.m */,
93D3970502644794E8A027BE /* MPNavigationController.h */,
- 93D395105935859D71679931 /* MPOverlayViewController.m */,
+ 93D39CC01630D0421205C4C4 /* MPNavigationController.m */,
93D39B455A71EC98C749E623 /* MPOverlayViewController.h */,
+ 93D395105935859D71679931 /* MPOverlayViewController.m */,
+ 93D39975CE5AEC99E3F086C7 /* MPPasswordCell.h */,
+ 93D39DEA995041A13DC9CAF7 /* MPPasswordCell.m */,
+ 93D39C44361BE57AF0B3071F /* MPPasswordsSegue.h */,
+ 93D39E7A12CC352B2825AA66 /* MPPasswordsSegue.m */,
+ 93D3914D7597F9A28DB9D85E /* MPPasswordsViewController.h */,
+ 93D3924EE15017F8A12CB436 /* MPPasswordsViewController.m */,
+ 93D392876BE5C011DE73B43F /* MPPopdownSegue.h */,
+ 93D39B050DD5F55E9794EFD4 /* MPPopdownSegue.m */,
+ DABD3BEA1711E2DC00CF925C /* MPPreferencesViewController.h */,
+ DABD3BEB1711E2DC00CF925C /* MPPreferencesViewController.m */,
+ 93D39730673227EFF6DEFF19 /* MPSetupViewController.h */,
+ 93D39A28369954D147E239BA /* MPSetupViewController.m */,
+ DABD3BEC1711E2DC00CF925C /* MPTypeViewController.h */,
+ DABD3BED1711E2DC00CF925C /* MPTypeViewController.m */,
+ 93D3971FE104BB4052484151 /* MPUsersViewController.h */,
+ 93D399E571F61E50A9BF8FAF /* MPUsersViewController.m */,
+ 93D39F556F2F142740A65E59 /* MPWebViewController.h */,
+ 93D3990E0CD1B5CF9FBB2C07 /* MPWebViewController.m */,
+ DABD3BF91711E2DC00CF925C /* Settings.bundle */,
+ DA38D6A218CCB5BF009AEB3E /* Storyboard.storyboard */,
);
path = iOS;
sourceTree = "";
@@ -3503,6 +3511,7 @@
DABD3B961711E29800CF925C /* pull-down@2x.png in Resources */,
DABD3B971711E29800CF925C /* pull-up.png in Resources */,
DA24EBEA19DAD6EE00FF010B /* Icon-Small.png in Resources */,
+ DA8495321A93049300B3053D /* icon_down@2x.png in Resources */,
DABD3B981711E29800CF925C /* pull-up@2x.png in Resources */,
DA7304A0194E022B00E72520 /* ui_textfield@2x.png in Resources */,
DAA1765119D8B82B0044227B /* copy_pw@2x.png in Resources */,
@@ -3526,6 +3535,7 @@
DABD3FCF1714F45C00CF925C /* identity@2x.png in Resources */,
DAA1764619D8B82B0044227B /* name_new.png in Resources */,
DA45224B190628B2008F650A /* icon_gear.png in Resources */,
+ DA8495311A93049300B3053D /* icon_down.png in Resources */,
DA25C5FF197DBF200046CDCF /* icon_thumbs-up@2x.png in Resources */,
DAE1EF2217E942DE00BC0086 /* Localizable.strings in Resources */,
DA38D6A318CCB5BF009AEB3E /* Storyboard.storyboard in Resources */,
@@ -3656,6 +3666,7 @@
93D39943D01E70DAC3B0DF76 /* mpw-util.c in Sources */,
93D39577FD8BB0945DB2F0A3 /* MPAlgorithmV3.m in Sources */,
93D39E5F7F6D7F5C0FAD090F /* MPTypes.m in Sources */,
+ 93D39508A6814612A5B3C226 /* MPMessageViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -4423,6 +4434,7 @@
DA32D00119CF4735004F3F0E /* MasterPassword.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
+ DA8495271A9146E600B3053D /* MasterPassword 7.xcdatamodel */,
DA32D00219CF4735004F3F0E /* MasterPassword 1.xcdatamodel */,
DA32D00319CF4735004F3F0E /* MasterPassword 2.xcdatamodel */,
DA32D00419CF4735004F3F0E /* MasterPassword 3.xcdatamodel */,
@@ -4430,7 +4442,7 @@
DA32D00619CF4735004F3F0E /* MasterPassword 5.xcdatamodel */,
DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */,
);
- currentVersion = DA32D00719CF4735004F3F0E /* MasterPassword 6.xcdatamodel */;
+ currentVersion = DA8495271A9146E600B3053D /* MasterPassword 7.xcdatamodel */;
path = MasterPassword.xcdatamodeld;
sourceTree = "";
versionGroupType = wrapper.xcdatamodel;
diff --git a/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist b/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist
index c0e333e3..981c5279 100644
--- a/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist
+++ b/MasterPassword/ObjC/iOS/Settings.bundle/Root.plist
@@ -147,6 +147,24 @@ To see a site's password anyway, tap and hold your finger down for a while
Type
PSToggleSwitchSpecifier
+
+ FooterText
+ When site downgrades are enabled, a long tap on the upgrade button will downgrade the site instead. This is useful if you accidentally upgraded a site and need to downgrade it again temporarily to see your old password.
+ Title
+
+ Type
+ PSGroupSpecifier
+
+
+ DefaultValue
+
+ Key
+ allowDowngrade
+ Title
+ Allow Downgrade
+ Type
+ PSToggleSwitchSpecifier
+
Type
PSGroupSpecifier
diff --git a/MasterPassword/ObjC/iOS/Storyboard.storyboard b/MasterPassword/ObjC/iOS/Storyboard.storyboard
index 120847c0..0eacb454 100644
--- a/MasterPassword/ObjC/iOS/Storyboard.storyboard
+++ b/MasterPassword/ObjC/iOS/Storyboard.storyboard
@@ -1,10 +1,11 @@
-
+
+
@@ -31,6 +32,7 @@
Exo2.0-Bold
Exo2.0-Bold
Exo2.0-Bold
+ Exo2.0-Bold
Exo2.0-ExtraBold
@@ -182,10 +184,10 @@
-
+
-
-
+
@@ -1761,7 +1765,7 @@ eg. apple.com, rmitchell@twitter.com
-
+
@@ -1769,7 +1773,7 @@ eg. apple.com, rmitchell@twitter.com
-
+
@@ -1778,7 +1782,7 @@ eg. apple.com, rmitchell@twitter.com
-
+
@@ -1787,7 +1791,7 @@ eg. apple.com, rmitchell@twitter.com
-
+
@@ -1796,7 +1800,7 @@ eg. apple.com, rmitchell@twitter.com
-
+
@@ -1811,7 +1815,7 @@ eg. apple.com, rmitchell@twitter.com
-
+
@@ -1819,13 +1823,13 @@ eg. apple.com, rmitchell@twitter.com
-
+
-
+
@@ -1866,10 +1870,10 @@ eg. apple.com, rmitchell@twitter.com
-
+
-
+
@@ -1880,7 +1884,7 @@ eg. apple.com, rmitchell@twitter.com
-
+
@@ -1984,13 +1988,13 @@ eg. apple.com, rmitchell@twitter.com
+
-
@@ -2082,7 +2086,7 @@ eg. apple.com, rmitchell@twitter.com
-
+
@@ -2122,13 +2126,13 @@ eg. apple.com, rmitchell@twitter.com
-
+
-
+
CgoKCgoKCgoKCgoKCg
@@ -2308,7 +2312,7 @@ Suspendisse potenti. Etiam ut nisi id augue tempor ultrices et sit amet sapien.
-
+
The right balance between security and convenience is often very personal.
@@ -2392,7 +2396,7 @@ However, it means that anyone who finds your device unlocked can do the same.
-
+
@@ -2580,7 +2584,7 @@ See
-
+
@@ -2645,7 +2649,7 @@ See
-
+
@@ -2691,7 +2695,7 @@ See
-
+
@@ -2710,13 +2714,13 @@ See
-
+
-
+
@@ -2756,7 +2760,7 @@ See
-
+
@@ -2775,13 +2779,13 @@ See
-
+
-
+
@@ -2821,13 +2825,13 @@ See
-
+
-
+
You really love Master Password and how it's solving your password problems. You're eager to encourage the maintenance, technical support and development of new features. I am a one-man shop, fuel enables me to allocate more work hours to Master Password.
@@ -2840,22 +2844,22 @@ UPCOMING:
-
+
-
+
-
+
-
+
-
+
@@ -2868,7 +2872,7 @@ UPCOMING:
-
+
fuel left: 0.3 work hours
invested: 3.7 work hours
@@ -2920,7 +2924,7 @@ invested: 3.7 work hours
-
+
@@ -2944,7 +2948,7 @@ invested: 3.7 work hours
-
+
@@ -2954,7 +2958,7 @@ invested: 3.7 work hours
-
+
@@ -2964,7 +2968,7 @@ invested: 3.7 work hours
-
+
@@ -3042,13 +3046,13 @@ invested: 3.7 work hours
-
+
-
+
@@ -3132,7 +3136,7 @@ invested: 3.7 work hours
-
+
@@ -3143,13 +3147,13 @@ invested: 3.7 work hours
-
+
-
+
@@ -3242,7 +3246,7 @@ invested: 3.7 work hours
-
+
To make it easy for you to recognize and copy passwords manually using a keyboard or other means, Master Password makes your site passwords visible on your screen by default.
@@ -3299,6 +3303,198 @@ You can temporarily reveal a password by holding your finger down on the site's
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Upgrading a site allows it to take advantage of
+the latest improvements in the Master Password algorithm.
+
+
+
+
+
+
+
+
+
+
+When you upgrade a site,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ . To upgrade a site, first log into the site, navigate to your account preferences where you can change the site's password. Make sure you fill in any "current password" fields on the website first, then press the upgrade button here to get your new site password.
+
+
+
+
+
+
+
+
+
+
+You can then update your site's account with the new and stronger password.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Scripts/updatePlist b/Scripts/updatePlist
index aabaf3e5..82b85c8d 100755
--- a/Scripts/updatePlist
+++ b/Scripts/updatePlist
@@ -51,10 +51,11 @@ setSettingWithTitle "Copyright" "$(getPlistWithKey NSHumanReadableCopyright)"
if [[ $DEPLOYMENT_LOCATION = YES ]]; then
# This build is a release. Do some release checks.
+ crashlyticsPlist="$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Crashlytics.plist"
passed=1
[[ $description != *-dirty ]] || \
{ passed=0; err 'ERROR: Cannot release a dirty version, first commit any changes.'; }
- [[ $(PlistBuddy -c "Print :'API Key'" "$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Crashlytics.plist") ]] || \
+ [[ -r "$crashlyticsPlist" && $(PlistBuddy -c "Print :'API Key'" "$crashlyticsPlist" 2>/dev/null) ]] || \
{ passed=0; err 'ERROR: Cannot release: Crashlytics API key is missing.'; }
(( passed )) || \
{ ftl "Failed to pass release checks. Fix the above errors and re-try. Aborting."; exit 1; }
diff --git a/Site/2013-05/img/android.png b/Site/2013-05/img/android.png
index 839005d9..86646a36 100644
Binary files a/Site/2013-05/img/android.png and b/Site/2013-05/img/android.png differ
diff --git a/Site/2013-05/img/c-cli.png b/Site/2013-05/img/c-cli.png
index 7f60eab2..1302622c 100644
Binary files a/Site/2013-05/img/c-cli.png and b/Site/2013-05/img/c-cli.png differ
diff --git a/Site/2013-05/img/java-gui.png b/Site/2013-05/img/java-gui.png
index 71a99739..3fc73faa 100644
Binary files a/Site/2013-05/img/java-gui.png and b/Site/2013-05/img/java-gui.png differ
diff --git a/Site/2013-05/index.html b/Site/2013-05/index.html
index 48981b0a..d6ed925f 100644
--- a/Site/2013-05/index.html
+++ b/Site/2013-05/index.html
@@ -83,7 +83,7 @@
Mac App Store,
Homebrew |
Desktop (Java) |
- Terminal (Native C) |
+ Terminal (C) |
Android (Beta) |
Web (Beta)
diff --git a/Site/2013-05/masterpassword-android-2.2.apk b/Site/2013-05/masterpassword-android-2.2.apk
new file mode 100644
index 00000000..7f1df67b
Binary files /dev/null and b/Site/2013-05/masterpassword-android-2.2.apk differ
diff --git a/Site/2013-05/masterpassword-android.apk b/Site/2013-05/masterpassword-android.apk
deleted file mode 100644
index eaf9180b..00000000
Binary files a/Site/2013-05/masterpassword-android.apk and /dev/null differ
diff --git a/Site/2013-05/masterpassword-android.apk b/Site/2013-05/masterpassword-android.apk
new file mode 120000
index 00000000..aa0ebe8a
--- /dev/null
+++ b/Site/2013-05/masterpassword-android.apk
@@ -0,0 +1 @@
+masterpassword-android-2.2.apk
\ No newline at end of file
diff --git a/Site/2013-05/masterpassword-gui-2.2.jar b/Site/2013-05/masterpassword-gui-2.2.jar
new file mode 100644
index 00000000..c08f7e3e
Binary files /dev/null and b/Site/2013-05/masterpassword-gui-2.2.jar differ
diff --git a/Site/2013-05/masterpassword-gui.jar b/Site/2013-05/masterpassword-gui.jar
deleted file mode 100644
index 953452e4..00000000
Binary files a/Site/2013-05/masterpassword-gui.jar and /dev/null differ
diff --git a/Site/2013-05/masterpassword-gui.jar b/Site/2013-05/masterpassword-gui.jar
new file mode 120000
index 00000000..1e02fddc
--- /dev/null
+++ b/Site/2013-05/masterpassword-gui.jar
@@ -0,0 +1 @@
+masterpassword-gui-2.2.jar
\ No newline at end of file
diff --git a/Site/2013-05/masterpassword-gui.jar.rev b/Site/2013-05/masterpassword-gui.jar.rev
index 85d89885..0861d083 100644
--- a/Site/2013-05/masterpassword-gui.jar.rev
+++ b/Site/2013-05/masterpassword-gui.jar.rev
@@ -1 +1 @@
-3932857c2117b8a399fdcb4fa888e9dc1a6818ee
+feddd038e36c72f8f5af2ed033f414aee1e97e2e
diff --git a/Site/mpw-js/index.html b/Site/mpw-js/index.html
index 657037f2..e46fbc21 100644
--- a/Site/mpw-js/index.html
+++ b/Site/mpw-js/index.html
@@ -12,7 +12,7 @@