2
0

Merge branch 'master' of github.com:Lyndir/MasterPassword

Conflicts:
	Site/2013-05/index.html
This commit is contained in:
Maarten Billemont 2015-03-10 14:39:05 -04:00
commit 5af3ffa178
152 changed files with 1556 additions and 787 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# OS-Specific junk. # OS-Specific junk.
.DS_Store .DS_Store
Thumbs.db
# IntelliJ # IntelliJ
/MasterPassword/Java/.idea /MasterPassword/Java/.idea

View File

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" /> <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
</project> <file url="file://$PROJECT_DIR$/MasterPassword/Java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-algorithm" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-cli" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-gui" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-model" charset="UTF-8" />
</component>
</project>

View File

@ -218,3 +218,8 @@ OBJC_EXTERN void CLSNSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash; - (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
@end @end
/**
* `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, simply use `Crashlytics()`
*/
#define CrashlyticsKit [Crashlytics sharedInstance]

View File

@ -15,13 +15,13 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>2.2.5</string> <string>2.2.9</string>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
<array> <array>
<string>iPhoneOS</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>40</string> <string>44</string>
<key>DTPlatformName</key> <key>DTPlatformName</key>
<string>iphoneos</string> <string>iphoneos</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>

Binary file not shown.

Binary file not shown.

View File

@ -39,7 +39,7 @@ else
fi fi
# Optional features. # Optional features.
mpw_color=0 # Colorized Identicon, requires libncurses-dev mpw_color=1 # Colorized Identicon, requires libncurses-dev
### DEPENDENCIES ### DEPENDENCIES
@ -204,11 +204,11 @@ mpw() {
echo echo
echo "Building target: $target..." echo "Building target: $target..."
CFLAGS=( local CFLAGS=(
# include paths # include paths
-I"lib/include" -I"lib/include"
) )
LDFLAGS=( local LDFLAGS=(
# scrypt # scrypt
"lib/scrypt/scrypt-crypto_aesctr.o" "lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o" "lib/scrypt/scrypt-sha256.o"
@ -240,11 +240,11 @@ mpw-bench() {
echo echo
echo "Building target: $target..." echo "Building target: $target..."
CFLAGS=( local CFLAGS=(
# include paths # include paths
-I"lib/include" -I"lib/include"
) )
LDFLAGS=( local LDFLAGS=(
# scrypt # scrypt
"lib/scrypt/scrypt-crypto_aesctr.o" "lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o" "lib/scrypt/scrypt-sha256.o"
@ -264,6 +264,9 @@ mpw-bench() {
-l"crypto" -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" \ cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench" "${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
echo "done! Now use ./mpw-bench" echo "done! Now use ./mpw-bench"
@ -276,13 +279,13 @@ mpw-tests() {
echo echo
echo "Building target: $target..." echo "Building target: $target..."
CFLAGS=( local CFLAGS=(
# include paths # include paths
-I"lib/include" -I"lib/include"
-I"/usr/include/libxml2" -I"/usr/include/libxml2"
-I"/usr/local/include/libxml2" -I"/usr/local/include/libxml2"
) )
LDFLAGS=( local LDFLAGS=(
# scrypt # scrypt
"lib/scrypt/scrypt-crypto_aesctr.o" "lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o" "lib/scrypt/scrypt-sha256.o"
@ -296,7 +299,10 @@ mpw-tests() {
-l"crypto" -l"xml2" -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" \ cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
"${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests" "${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests"
echo "done! Now use ./mpw-tests" echo "done! Now use ./mpw-tests"

View File

@ -11,6 +11,7 @@
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
#define MP_N 32768 #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) { static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); 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( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope ); 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 MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant ); const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "algorithm: v%d\n", 0 );
trc( "siteName: %s\n", siteName ); trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter ); trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant ); trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType ); trc( "siteType: %d\n", siteType );
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext ); trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": 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. // Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )

View File

@ -11,6 +11,7 @@
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
#define MP_N 32768 #define MP_N 32768
@ -21,7 +22,8 @@
static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) { static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); 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( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope ); 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 MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant ); const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "algorithm: v%d\n", 1 );
trc( "siteName: %s\n", siteName ); trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter ); trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant ); trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType ); trc( "siteType: %d\n", siteType );
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext ); trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": 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. // Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )

View File

@ -11,6 +11,7 @@
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
#define MP_N 32768 #define MP_N 32768
@ -21,7 +22,8 @@
static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) { static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); 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( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope ); 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 MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant ); const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "algorithm: v%d\n", 2 );
trc( "siteName: %s\n", siteName ); trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter ); trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant ); trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType ); trc( "siteType: %d\n", siteType );
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext ); trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": 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. // Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )

View File

@ -11,6 +11,7 @@
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include "mpw-types.h"
#include "mpw-util.h" #include "mpw-util.h"
#define MP_N 32768 #define MP_N 32768
@ -21,7 +22,8 @@
static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) { static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword ); 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( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope ); 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 MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant ); const char *siteScope = mpw_scopeForVariant( siteVariant );
trc( "algorithm: v%d\n", 3 );
trc( "siteName: %s\n", siteName ); trc( "siteName: %s\n", siteName );
trc( "siteCounter: %d\n", siteCounter ); trc( "siteCounter: %d\n", siteCounter );
trc( "siteVariant: %d\n", siteVariant ); trc( "siteVariant: %d\n", siteVariant );
trc( "siteType: %d\n", siteType ); trc( "siteType: %d\n", siteType );
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext ); trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": 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. // Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext ) // sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )

View File

@ -25,7 +25,7 @@
static void usage() { 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" fprintf( stderr, " -u name Specify the full name of the user.\n"
" Defaults to %s in env.\n\n", MP_env_fullname ); " Defaults to %s in env.\n\n", MP_env_fullname );
fprintf( stderr, " -t type Specify the password's template.\n" fprintf( stderr, " -t type Specify the password's template.\n"

View File

@ -21,6 +21,7 @@ int main(int argc, char *const argv[]) {
// Read in the test case. // Read in the test case.
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" ); xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
uint32_t algorithm = mpw_xmlTestCaseInteger( testCase, "algorithm" );
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" ); xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" ); xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" ); xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
@ -36,16 +37,20 @@ int main(int argc, char *const argv[]) {
// Run the test case. // Run the test case.
fprintf( stdout, "test case %s... ", id ); fprintf( stdout, "test case %s... ", id );
if (!xmlStrlen( result )) {
fprintf( stdout, "abstract.\n" );
continue;
}
// 1. calculate the master key. // 1. calculate the master key.
const uint8_t *masterKey = mpw_masterKeyForUser( const uint8_t *masterKey = mpw_masterKeyForUser(
(char *)fullName, (char *)masterPassword, MPAlgorithmVersionCurrent ); (char *)fullName, (char *)masterPassword, algorithm );
if (!masterKey) if (!masterKey)
ftl( "Couldn't derive master key." ); ftl( "Couldn't derive master key." );
// 2. calculate the site password. // 2. calculate the site password.
const char *sitePassword = mpw_passwordForSite( 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 ); mpw_free( masterKey, MP_dkLen );
if (!sitePassword) if (!sitePassword)
ftl( "Couldn't derive site password." ); ftl( "Couldn't derive site password." );
@ -56,7 +61,7 @@ int main(int argc, char *const argv[]) {
else { else {
++failedTests; ++failedTests;
fprintf( stdout, "FAILED! (result %s != expected %s)\n", result, sitePassword ); fprintf( stdout, "FAILED! (got %s != expected %s)\n", sitePassword, result );
} }
// Free test case. // Free test case.

View File

@ -21,15 +21,17 @@
const MPSiteType mpw_typeWithName(const char *typeName) { 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 ); size_t stdTypeNameSize = strlen( typeName );
char stdTypeName[strlen( typeName )]; if (strstr(typeName, "Generated" ) == typeName)
if (stdTypeNameSize > strlen( "generated" )) stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
strcpy( stdTypeName, typeName + strlen( "generated" ) ); char stdTypeName[stdTypeNameSize + 1];
else for (size_t c = 0; c < stdTypeNameSize; ++c)
strcpy( stdTypeName, typeName ); stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] );
for (char *tN = stdTypeName; *tN; ++tN) stdTypeName[stdTypeNameSize] = '\0';
*tN = (char)tolower( *tN );
// Find what site type is represented by the type name.
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" )) if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
return MPSiteTypeGeneratedMaximum; return MPSiteTypeGeneratedMaximum;
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" )) 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" )) if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
return MPSiteTypeGeneratedPhrase; return MPSiteTypeGeneratedPhrase;
fprintf( stderr, "Not a generated type name: %s", stdTypeName ); ftl( "Not a generated type name: %s", stdTypeName );
abort();
} }
inline const char **mpw_templatesForType(MPSiteType type, size_t *count) { const char **mpw_templatesForType(MPSiteType type, size_t *count) {
if (!(type & MPSiteTypeClassGenerated)) { if (!(type & MPSiteTypeClassGenerated)) {
ftl( "Not a generated type: %d", type ); ftl( "Not a generated type: %d", type );
@ -61,42 +62,42 @@ inline const char **mpw_templatesForType(MPSiteType type, size_t *count) {
switch (type) { switch (type) {
case MPSiteTypeGeneratedMaximum: { case MPSiteTypeGeneratedMaximum: {
*count = 2; return alloc_array( *count, const char *,
return (const char *[]){ "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" }; "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
} }
case MPSiteTypeGeneratedLong: { case MPSiteTypeGeneratedLong: {
*count = 21; return alloc_array( *count, const char *,
return (const char *[]){ "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno", "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno", "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno", "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno", "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno", "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno", "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" }; "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
} }
case MPSiteTypeGeneratedMedium: { case MPSiteTypeGeneratedMedium: {
*count = 2; return alloc_array( *count, const char *,
return (const char *[]){ "CvcnoCvc", "CvcCvcno" }; "CvcnoCvc", "CvcCvcno" );
} }
case MPSiteTypeGeneratedBasic: { case MPSiteTypeGeneratedBasic: {
*count = 3; return alloc_array( *count, const char *,
return (const char *[]){ "aaanaaan", "aannaaan", "aaannaaa" }; "aaanaaan", "aannaaan", "aaannaaa" );
} }
case MPSiteTypeGeneratedShort: { case MPSiteTypeGeneratedShort: {
*count = 1; return alloc_array( *count, const char *,
return (const char *[]){"Cvcn"}; "Cvcn" );
} }
case MPSiteTypeGeneratedPIN: { case MPSiteTypeGeneratedPIN: {
*count = 1; return alloc_array( *count, const char *,
return (const char *[]){ "nnnn" }; "nnnn" );
} }
case MPSiteTypeGeneratedName: { case MPSiteTypeGeneratedName: {
*count = 1; return alloc_array( *count, const char *,
return (const char *[]) {"cvccvcvcv"}; "cvccvcvcv" );
} }
case MPSiteTypeGeneratedPhrase: { case MPSiteTypeGeneratedPhrase: {
*count = 3; return alloc_array( *count, const char *,
return (const char *[]){ "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" }; "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
} }
default: { default: {
ftl( "Unknown generated type: %d", type ); ftl( "Unknown generated type: %d", type );
@ -113,15 +114,19 @@ const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
if (!count) if (!count)
return NULL; return NULL;
return templates[seedByte % count]; char const *template = templates[seedByte % count];
free( templates );
return template;
} }
const MPSiteVariant mpw_variantWithName(const char *variantName) { const MPSiteVariant mpw_variantWithName(const char *variantName) {
char stdVariantName[strlen( variantName )]; // Lower-case and trim optionally leading "generated" string from typeName to standardize it.
strcpy( stdVariantName, variantName ); size_t stdVariantNameSize = strlen( variantName );
for (char *vN = stdVariantName; *vN; ++vN) char stdVariantName[stdVariantNameSize + 1];
*vN = (char)tolower( *vN ); 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" )) if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
return MPSiteVariantPassword; return MPSiteVariantPassword;

View File

@ -6,6 +6,8 @@
// Copyright (c) 2014 Lyndir. All rights reserved. // Copyright (c) 2014 Lyndir. All rights reserved.
// //
#ifndef _MPW_TYPES_H
#define _MPW_TYPES_H
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
@ -73,9 +75,10 @@ const char *mpw_scopeForVariant(MPSiteVariant variant);
const MPSiteType mpw_typeWithName(const char *typeName); 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. * 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. * 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); 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); const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
#endif // _MPW_TYPES_H

View File

@ -9,7 +9,12 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <locale.h>
#ifdef COLOR
#include <unistd.h>
#include <curses.h>
#include <term.h>
#endif
#include <scrypt/sha256.h> #include <scrypt/sha256.h>
#include <scrypt/crypto_scrypt.h> #include <scrypt/crypto_scrypt.h>
@ -91,20 +96,31 @@ const char *mpw_idForBuf(const void *buf, size_t length) {
return mpw_hex( hash, 32 ); 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) { const char *mpw_hex(const void *buf, size_t length) {
mpw_hex_buf = realloc( mpw_hex_buf, length * 2 + 1 ); if (!mpw_hex_buf) {
for (size_t kH = 0; kH < length; kH++) mpw_hex_buf = malloc( 10 * sizeof( char* ) );
sprintf( &(mpw_hex_buf[kH * 2]), "%02X", ((const uint8_t *)buf)[kH] ); 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 #ifdef COLOR
static int putvari; static int putvari;
static char *putvarc = NULL; static char *putvarc = NULL;
static bool istermsetup = false; static int istermsetup = 0;
static void initputvar() { static void initputvar() {
if (putvarc) if (putvarc)
free(putvarc); free(putvarc);
@ -165,8 +181,33 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
return identicon; 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" ); if (!utf8Byte)
return mbstowcs( NULL, string, strlen( string ) ); 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;
} }

View File

@ -25,6 +25,14 @@
//// Buffers and memory. //// 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. */ /** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
void mpw_pushBuf( void mpw_pushBuf(
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize); 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. /** Encode a buffer as a string of hexadecimal characters.
* @return A C-string in a reused buffer, do not free or store it. */ * @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(const void *buf, size_t length);
const char *mpw_hex_l(uint32_t number);
/** Encode a fingerprint for a buffer. /** Encode a fingerprint for a buffer.
* @return A C-string in a reused buffer, do not free or store it. */ * @return A C-string in a reused buffer, do not free or store it. */
const char *mpw_idForBuf(const void *buf, size_t length); 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. //// 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);

View File

@ -13,7 +13,6 @@
<name>Master Password Algorithm Implementation</name> <name>Master Password Algorithm Implementation</name>
<description>The implementation of the Master Password algorithm</description> <description>The implementation of the Master Password algorithm</description>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-algorithm</artifactId> <artifactId>masterpassword-algorithm</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -6,6 +6,7 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;
/** /**
@ -147,7 +148,8 @@ public enum MPSiteType {
* *
* @return The type registered with the given name. * @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) if (name == null)
return null; return null;

View File

@ -3,6 +3,8 @@ package com.lyndir.masterpassword;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List; 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. * @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) if (name == null)
return null; return null;

View File

@ -3,10 +3,8 @@ package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.lyndir.lhunath.opal.system.util.MetaObject; import com.lyndir.lhunath.opal.system.util.MetaObject;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**

View File

@ -1,8 +1,6 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import com.lyndir.lhunath.opal.system.util.ObjectMeta;
/** /**

View File

@ -54,7 +54,7 @@ public abstract class MasterKey {
@Nullable @Nullable
protected abstract byte[] deriveKey(final char[] masterPassword); protected abstract byte[] deriveKey(final char[] masterPassword);
protected abstract Version getAlgorithm(); public abstract Version getAlgorithmVersion();
@NotNull @NotNull
public String getFullName() { public String getFullName() {
@ -63,18 +63,18 @@ public abstract class MasterKey {
} }
@Nonnull @Nonnull
protected byte[] getMasterKey() { protected byte[] getKey() {
return Preconditions.checkNotNull( masterKey ); return Preconditions.checkNotNull( masterKey );
} }
public byte[] getKeyID() { 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, public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, int siteCounter,
@Nullable final String siteContext); final MPSiteVariant siteVariant, @Nullable final String siteContext);
public boolean isValid() { public boolean isValid() {
return masterKey != null; return masterKey != null;
@ -95,8 +95,12 @@ public abstract class MasterKey {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
masterKey = deriveKey( masterPassword ); 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; return this;
} }

View File

@ -41,7 +41,7 @@ public class MasterKeyV0 extends MasterKey {
} }
@Override @Override
protected Version getAlgorithm() { public Version getAlgorithmVersion() {
return Version.V0; return Version.V0;
} }
@ -89,29 +89,37 @@ public class MasterKeyV0 extends MasterKey {
byte[] siteNameBytes = siteName.getBytes( MP_charset ); byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() ); byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter ); 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 ); byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext ); logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), 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 ); byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null) if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
byte[] sitePasswordSeed = MP_mac.of( getMasterKey(), sitePasswordInfo ); byte[] sitePasswordSeedBytes = MP_mac.of( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) ); 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 ); Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFFFF; int templateIndex = sitePasswordSeed[0];
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex ); MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() ); logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() ); StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) { for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFFFF; int characterIndex = sitePasswordSeed[i + 1];
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i ); MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex ); char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex, logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,

View File

@ -1,15 +1,9 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.*; import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -30,7 +24,7 @@ public class MasterKeyV1 extends MasterKeyV0 {
} }
@Override @Override
protected Version getAlgorithm() { public Version getAlgorithmVersion() {
return Version.V1; return Version.V1;
} }
@ -52,19 +46,19 @@ public class MasterKeyV1 extends MasterKeyV0 {
byte[] siteNameBytes = siteName.getBytes( MP_charset ); byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteName.length() ); byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
byte[] siteCounterBytes = bytesForInt( siteCounter ); 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 ); byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext ); logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), 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 ); byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null) if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); 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 ) ) ); logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 ); Preconditions.checkState( sitePasswordSeed.length > 0 );

View File

@ -23,7 +23,7 @@ public class MasterKeyV2 extends MasterKeyV1 {
} }
@Override @Override
protected Version getAlgorithm() { public Version getAlgorithmVersion() {
return Version.V2; return Version.V2;
} }
@ -45,19 +45,19 @@ public class MasterKeyV2 extends MasterKeyV1 {
byte[] siteNameBytes = siteName.getBytes( MP_charset ); byte[] siteNameBytes = siteName.getBytes( MP_charset );
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length ); byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
byte[] siteCounterBytes = bytesForInt( siteCounter ); 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 ); byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext ); logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ), logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ), 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 ); byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
if (siteContextBytes != null) if (siteContextBytes != null)
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes ); sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) ); 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 ) ) ); logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 ); Preconditions.checkState( sitePasswordSeed.length > 0 );

View File

@ -26,7 +26,7 @@ public class MasterKeyV3 extends MasterKeyV2 {
} }
@Override @Override
protected Version getAlgorithm() { public Version getAlgorithmVersion() {
return Version.V3; return Version.V3;
} }

View File

@ -1,10 +1,10 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.NNSupplier; import com.lyndir.lhunath.opal.system.util.*;
import com.lyndir.lhunath.opal.system.util.NSupplier;
import java.util.List; import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -25,8 +25,9 @@ public class MPWTests {
@XmlElement(name = "case") @XmlElement(name = "case")
private List<Case> cases; private List<Case> cases;
@Nonnull
public List<Case> getCases() { public List<Case> getCases() {
return cases; return checkNotNull( cases );
} }
public Case getCase(String identifier) { public Case getCase(String identifier) {
@ -45,6 +46,8 @@ public class MPWTests {
@XmlAttribute @XmlAttribute
private String parent; private String parent;
@XmlElement @XmlElement
private String algorithm;
@XmlElement
private String fullName; private String fullName;
@XmlElement @XmlElement
private String masterPassword; private String masterPassword;
@ -65,76 +68,86 @@ public class MPWTests {
private transient Case parentCase; private transient Case parentCase;
public void setTests(MPWTests tests) { public void initializeParentHierarchy(MPWTests tests) {
if (parent != null) { if (parent != null) {
parentCase = tests.getCase( parent ); parentCase = tests.getCase( parent );
fullName = ifNotNullElse( fullName, new NNSupplier<String>() { parentCase.initializeParentHierarchy( tests );
@Nonnull
@Override
public String get() {
return parentCase.getFullName();
}
} );
masterPassword = ifNotNullElse( masterPassword, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return new String( parentCase.getMasterPassword() );
}
} );
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase.getKeyID();
}
} );
siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase.getSiteName();
}
} );
siteCounter = ifNotNullElse( siteCounter, new NNSupplier<Integer>() {
@Nonnull
@Override
public Integer get() {
return parentCase.getSiteCounter();
}
} );
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase.getSiteType().name();
}
} );
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase.getSiteVariant().name();
}
} );
siteContext = ifNotNullElseNullable( siteContext, new NSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase.getSiteContext();
}
} );
result = ifNotNullElse( result, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase.getResult();
}
} );
} }
algorithm = ifNotNullElse( algorithm, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.algorithm );
}
} );
fullName = ifNotNullElse( fullName, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.fullName );
}
} );
masterPassword = ifNotNullElse( masterPassword, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.masterPassword );
}
} );
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.keyID );
}
} );
siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.siteName );
}
} );
siteCounter = ifNotNullElse( siteCounter, new NNSupplier<Integer>() {
@Nonnull
@Override
public Integer get() {
return checkNotNull( parentCase.siteCounter );
}
} );
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.siteType );
}
} );
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return checkNotNull( parentCase.siteVariant );
}
} );
siteContext = ifNotNullElse( siteContext, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase == null? "": checkNotNull( parentCase.siteContext );
}
} );
result = ifNotNullElse( result, new NNSupplier<String>() {
@Nonnull
@Override
public String get() {
return parentCase == null? "": checkNotNull( parentCase.result );
}
} );
} }
@Nonnull
public String getIdentifier() { public String getIdentifier() {
return identifier; return identifier;
} }
@ -144,40 +157,53 @@ public class MPWTests {
return parentCase; return parentCase;
} }
@Nonnull
public MasterKey.Version getAlgorithm() {
return MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( algorithm ) );
}
@Nonnull
public String getFullName() { public String getFullName() {
return fullName; return checkNotNull( fullName );
} }
@Nonnull
public char[] getMasterPassword() { public char[] getMasterPassword() {
return masterPassword.toCharArray(); return checkNotNull( masterPassword ).toCharArray();
} }
@Nonnull
public String getKeyID() { public String getKeyID() {
return keyID; return checkNotNull( keyID );
} }
@Nonnull
public String getSiteName() { public String getSiteName() {
return siteName; return checkNotNull( siteName );
} }
public int getSiteCounter() { public int getSiteCounter() {
return siteCounter; return ifNotNullElse( siteCounter, 1 );
} }
@Nonnull
public MPSiteType getSiteType() { public MPSiteType getSiteType() {
return MPSiteType.forName( siteType ); return MPSiteType.forName( checkNotNull( siteType ) );
} }
@Nonnull
public MPSiteVariant getSiteVariant() { public MPSiteVariant getSiteVariant() {
return MPSiteVariant.forName( siteVariant ); return MPSiteVariant.forName( checkNotNull( siteVariant ) );
} }
@Nonnull
public String getSiteContext() { public String getSiteContext() {
return siteContext; return checkNotNull( siteContext );
} }
@Nonnull
public String getResult() { public String getResult() {
return result; return checkNotNull( result );
} }
@Override @Override

View File

@ -5,6 +5,7 @@ import static org.testng.Assert.*;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.StringUtils;
import java.net.URL; import java.net.URL;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBContext;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
@ -26,7 +27,7 @@ public class MasterKeyTest {
URL testCasesResource = Resources.getResource( "mpw_tests.xml" ); URL testCasesResource = Resources.getResource( "mpw_tests.xml" );
tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource ); tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource );
for (MPWTests.Case testCase : tests.getCases()) for (MPWTests.Case testCase : tests.getCases())
testCase.setTests( tests ); testCase.initializeParentHierarchy( tests );
defaultCase = tests.getCase( MPWTests.ID_DEFAULT ); defaultCase = tests.getCase( MPWTests.ID_DEFAULT );
} }
@ -35,10 +36,15 @@ public class MasterKeyTest {
throws Exception { throws Exception {
for (MPWTests.Case testCase : tests.getCases()) { 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( assertEquals(
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(), masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase ); testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
logger.inf( "passed!" );
} }
} }
@ -55,8 +61,13 @@ public class MasterKeyTest {
throws Exception { throws Exception {
for (MPWTests.Case testCase : tests.getCases()) { 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() ); MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase ); assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
logger.inf( "passed!" );
} }
} }

View File

@ -1,5 +1,7 @@
<tests> <tests>
<!-- Default values for all parameters. -->
<case id="default"> <case id="default">
<algorithm><!-- current --></algorithm>
<fullName>Robert Lee Mitchell</fullName> <fullName>Robert Lee Mitchell</fullName>
<masterPassword>banana colored duckling</masterPassword> <masterPassword>banana colored duckling</masterPassword>
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID> <keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
@ -7,67 +9,271 @@
<siteCounter>1</siteCounter> <siteCounter>1</siteCounter>
<siteType>GeneratedLong</siteType> <siteType>GeneratedLong</siteType>
<siteVariant>Password</siteVariant> <siteVariant>Password</siteVariant>
<result><!-- abstract --></result>
</case>
<!-- Algorithm 3 -->
<case id="v3" parent="default">
<algorithm>3</algorithm>
<result>Jejr5[RepuSosp</result> <result>Jejr5[RepuSosp</result>
</case> </case>
<case id="mb_fullName" parent="default"> <case id="v3_mb_fullName" parent="v3">
<fullName></fullName> <fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID> <keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>NopaDajh8=Fene</result> <result>NopaDajh8=Fene</result>
</case> </case>
<case id="mb_masterPassword" parent="default"> <case id="v3_mb_masterPassword" parent="v3">
<masterPassword></masterPassword> <masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID> <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result> <result>QesuHirv5-Xepl</result>
</case> </case>
<case id="mb_siteName" parent="default"> <case id="v3_mb_siteName" parent="v3">
<siteName></siteName> <siteName></siteName>
<result>LiheCuwhSerz6)</result> <result>LiheCuwhSerz6)</result>
</case> </case>
<case id="loginName" parent="default"> <case id="v3_loginName" parent="v3">
<siteVariant>Login</siteVariant> <siteVariant>Login</siteVariant>
<siteType>GeneratedName</siteType> <siteType>GeneratedName</siteType>
<result>wohzaqage</result> <result>wohzaqage</result>
</case> </case>
<case id="securityAnswer" parent="default"> <case id="v3_securityAnswer" parent="v3">
<siteVariant>Answer</siteVariant> <siteVariant>Answer</siteVariant>
<siteType>GeneratedPhrase</siteType> <siteType>GeneratedPhrase</siteType>
<result>xin diyjiqoja hubu</result> <result>xin diyjiqoja hubu</result>
</case> </case>
<case id="securityAnswer_context" parent="securityAnswer"> <case id="v3_securityAnswer_context" parent="v3_securityAnswer">
<siteContext>question</siteContext> <siteContext>question</siteContext>
<result>xogx tem cegyiva jab</result> <result>xogx tem cegyiva jab</result>
</case> </case>
<case id="type_maximum" parent="default"> <case id="v3_type_maximum" parent="v3">
<siteType>GeneratedMaximum</siteType> <siteType>GeneratedMaximum</siteType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result> <result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case> </case>
<case id="type_medium" parent="default"> <case id="v3_type_medium" parent="v3">
<siteType>GeneratedMedium</siteType> <siteType>GeneratedMedium</siteType>
<result>Jej2$Quv</result> <result>Jej2$Quv</result>
</case> </case>
<case id="type_basic" parent="default"> <case id="v3_type_basic" parent="v3">
<siteType>GeneratedBasic</siteType> <siteType>GeneratedBasic</siteType>
<result>WAo2xIg6</result> <result>WAo2xIg6</result>
</case> </case>
<case id="type_short" parent="default"> <case id="v3_type_short" parent="v3">
<siteType>GeneratedShort</siteType> <siteType>GeneratedShort</siteType>
<result>Jej2</result> <result>Jej2</result>
</case> </case>
<case id="type_pin" parent="default"> <case id="v3_type_pin" parent="v3">
<siteType>GeneratedPIN</siteType> <siteType>GeneratedPIN</siteType>
<result>7662</result> <result>7662</result>
</case> </case>
<case id="type_name" parent="default"> <case id="v3_type_name" parent="v3">
<siteType>GeneratedName</siteType> <siteType>GeneratedName</siteType>
<result>jejraquvo</result> <result>jejraquvo</result>
</case> </case>
<case id="type_phrase" parent="default"> <case id="v3_type_phrase" parent="v3">
<siteType>GeneratedPhrase</siteType> <siteType>GeneratedPhrase</siteType>
<result>jejr quv cabsibu tam</result> <result>jejr quv cabsibu tam</result>
</case> </case>
<case id="counter_ceiling" parent="default"> <case id="v3_counter_ceiling" parent="v3">
<siteCounter>4294967295</siteCounter> <siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result> <result>XambHoqo6[Peni</result>
</case> </case>
<!-- Algorithm 2 -->
<case id="v2" parent="default">
<algorithm>2</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v2_mb_fullName" parent="v2">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v2_mb_masterPassword" parent="v2">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v2_mb_siteName" parent="v2">
<siteName></siteName>
<result>LiheCuwhSerz6)</result>
</case>
<case id="v2_loginName" parent="v2">
<siteVariant>Login</siteVariant>
<siteType>GeneratedName</siteType>
<result>wohzaqage</result>
</case>
<case id="v2_securityAnswer" parent="v2">
<siteVariant>Answer</siteVariant>
<siteType>GeneratedPhrase</siteType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
<siteContext>question</siteContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v2_type_maximum" parent="v2">
<siteType>GeneratedMaximum</siteType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v2_type_medium" parent="v2">
<siteType>GeneratedMedium</siteType>
<result>Jej2$Quv</result>
</case>
<case id="v2_type_basic" parent="v2">
<siteType>GeneratedBasic</siteType>
<result>WAo2xIg6</result>
</case>
<case id="v2_type_short" parent="v2">
<siteType>GeneratedShort</siteType>
<result>Jej2</result>
</case>
<case id="v2_type_pin" parent="v2">
<siteType>GeneratedPIN</siteType>
<result>7662</result>
</case>
<case id="v2_type_name" parent="v2">
<siteType>GeneratedName</siteType>
<result>jejraquvo</result>
</case>
<case id="v2_type_phrase" parent="v2">
<siteType>GeneratedPhrase</siteType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v2_counter_ceiling" parent="v2">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 1 -->
<case id="v1" parent="default">
<algorithm>1</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v1_mb_fullName" parent="v1">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v1_mb_masterPassword" parent="v1">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v1_mb_siteName" parent="v1">
<siteName></siteName>
<result>WawiYarp2@Kodh</result>
</case>
<case id="v1_loginName" parent="v1">
<siteVariant>Login</siteVariant>
<siteType>GeneratedName</siteType>
<result>wohzaqage</result>
</case>
<case id="v1_securityAnswer" parent="v1">
<siteVariant>Answer</siteVariant>
<siteType>GeneratedPhrase</siteType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
<siteContext>question</siteContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v1_type_maximum" parent="v1">
<siteType>GeneratedMaximum</siteType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v1_type_medium" parent="v1">
<siteType>GeneratedMedium</siteType>
<result>Jej2$Quv</result>
</case>
<case id="v1_type_basic" parent="v1">
<siteType>GeneratedBasic</siteType>
<result>WAo2xIg6</result>
</case>
<case id="v1_type_short" parent="v1">
<siteType>GeneratedShort</siteType>
<result>Jej2</result>
</case>
<case id="v1_type_pin" parent="v1">
<siteType>GeneratedPIN</siteType>
<result>7662</result>
</case>
<case id="v1_type_name" parent="v1">
<siteType>GeneratedName</siteType>
<result>jejraquvo</result>
</case>
<case id="v1_type_phrase" parent="v1">
<siteType>GeneratedPhrase</siteType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v1_counter_ceiling" parent="v1">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 0 -->
<case id="v0" parent="default">
<algorithm>0</algorithm>
<result>Feji5@ReduWosh</result>
</case>
<case id="v0_mb_fullName" parent="v0">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>HajrYudo7@Mamh</result>
</case>
<case id="v0_mb_masterPassword" parent="v0">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>MewmDini0]Meho</result>
</case>
<case id="v0_mb_siteName" parent="v0">
<siteName></siteName>
<result>HahiVana2@Nole</result>
</case>
<case id="v0_loginName" parent="v0">
<siteVariant>Login</siteVariant>
<siteType>GeneratedName</siteType>
<result>lozwajave</result>
</case>
<case id="v0_securityAnswer" parent="v0">
<siteVariant>Answer</siteVariant>
<siteType>GeneratedPhrase</siteType>
<result>miy lirfijoja dubu</result>
</case>
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
<siteContext>question</siteContext>
<result>movm bex gevrica jaf</result>
</case>
<case id="v0_type_maximum" parent="v0">
<siteType>GeneratedMaximum</siteType>
<result>w1!3bA3icmRAc)SS@lwl</result>
</case>
<case id="v0_type_medium" parent="v0">
<siteType>GeneratedMedium</siteType>
<result>Fej7]Jug</result>
</case>
<case id="v0_type_basic" parent="v0">
<siteType>GeneratedBasic</siteType>
<result>wvH7irC1</result>
</case>
<case id="v0_type_short" parent="v0">
<siteType>GeneratedShort</siteType>
<result>Fej7</result>
</case>
<case id="v0_type_pin" parent="v0">
<siteType>GeneratedPIN</siteType>
<result>2117</result>
</case>
<case id="v0_type_name" parent="v0">
<siteType>GeneratedName</siteType>
<result>fejrajugo</result>
</case>
<case id="v0_type_phrase" parent="v0">
<siteType>GeneratedPhrase</siteType>
<result>fejr jug gabsibu bax</result>
</case>
<case id="v0_counter_ceiling" parent="v0">
<siteCounter>4294967295</siteCounter>
<result>QateDojh1@Hecn</result>
</case>
</tests> </tests>

View File

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lyndir.masterpassword" package="com.lyndir.masterpassword"
android:versionCode="1" android:versionCode="1"
android:versionName="GIT-SNAPSHOT"> android:versionName="2.2">
<uses-sdk <uses-sdk
android:minSdkVersion="19" android:minSdkVersion="19"

View File

@ -1,3 +0,0 @@
# File used by Eclipse to determine the target system
# Project target.
target=android-16

View File

@ -13,7 +13,6 @@
<name>Master Password Android</name> <name>Master Password Android</name>
<description>An Android application to the Master Password algorithm</description> <description>An Android application to the Master Password algorithm</description>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-android</artifactId> <artifactId>masterpassword-android</artifactId>
<packaging>apk</packaging> <packaging>apk</packaging>
@ -30,7 +29,7 @@
<skip>false</skip> <skip>false</skip>
</zipalign> </zipalign>
<sdk> <sdk>
<platform>19</platform> <platform>21</platform>
</sdk> </sdk>
</configuration> </configuration>
</plugin> </plugin>
@ -39,9 +38,32 @@
<profiles> <profiles>
<profile> <profile>
<id>sign</id> <id>release</id>
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<configuration>
<sign>
<debug>false</debug>
</sign>
</configuration>
<executions>
<execution>
<id>manifest-update</id>
<phase>process-resources</phase>
<goals>
<goal>manifest-update</goal>
</goals>
<configuration>
<manifestVersionCodeUpdateFromVersion>true</manifestVersionCodeUpdateFromVersion>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jarsigner-plugin</artifactId> <artifactId>maven-jarsigner-plugin</artifactId>
@ -54,14 +76,14 @@
<phase>package</phase> <phase>package</phase>
<inherited>true</inherited> <inherited>true</inherited>
<configuration> <configuration>
<archiveDirectory></archiveDirectory> <archiveDirectory />
<includes> <includes>
<include>target/*.apk</include> <include>target/*.apk</include>
</includes> </includes>
<keystore>release.jks</keystore> <keystore>release.jks</keystore>
<storepass>${env.PASSWORD}</storepass> <storepass>${env.PASSWORD}</storepass>
<keypass>${env.PASSWORD}</keypass> <keypass>${env.PASSWORD}</keypass>
<alias>android</alias> <alias>masterpassword-android</alias>
<arguments> <arguments>
<argument>-sigalg</argument><argument>MD5withRSA</argument> <argument>-sigalg</argument><argument>MD5withRSA</argument>
<argument>-digestalg</argument><argument>SHA1</argument> <argument>-digestalg</argument><argument>SHA1</argument>
@ -70,16 +92,6 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<sign>
<debug>false</debug>
</sign>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</profile> </profile>

View File

@ -0,0 +1 @@
/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:width="20dp"
android:height="20dp" />
</shape>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/background">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:fillViewport="true">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<View
android:layout_width="match_parent"
android:layout_height="0dp" />
<LinearLayout
android:id="@+id/users"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="@drawable/divider20"
android:showDividers="middle" />
<View
android:layout_width="match_parent"
android:layout_height="0dp" />
</LinearLayout>
</HorizontalScrollView>
</FrameLayout>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/userName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/avatar0"
android:drawablePadding="8dp"
android:text="Robert Lee Mitchell" />

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">Master Password</string> <string name="app_name">Master Password</string>
<string name="avatar">User Avatar</string>
<string name="remember">Remember</string> <string name="remember">Remember</string>
<string name="forgetOnClose">Forget on close</string> <string name="forgetOnClose">Forget on close</string>
<string name="maskPassword">Hide password</string> <string name="maskPassword">Hide password</string>

View File

@ -205,7 +205,7 @@ public class EmergencyActivity extends Activity {
final MasterKey.Version version = (MasterKey.Version) siteVersionField.getSelectedItem(); final MasterKey.Version version = (MasterKey.Version) siteVersionField.getSelectedItem();
try { try {
if (fullName.hashCode() == hc_userName && Arrays.hashCode( masterPassword ) == hc_masterPassword && if (fullName.hashCode() == hc_userName && Arrays.hashCode( masterPassword ) == hc_masterPassword &&
masterKeyFuture != null && masterKeyFuture.get().getAlgorithm() == version) masterKeyFuture != null && masterKeyFuture.get().getAlgorithmVersion() == version)
return; return;
} }
catch (InterruptedException | ExecutionException e) { catch (InterruptedException | ExecutionException e) {

View File

@ -1,7 +1,6 @@
package com.lyndir.masterpassword; package com.lyndir.masterpassword;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.Typeface; import android.graphics.Typeface;

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -13,7 +13,6 @@
<name>Master Password CLI</name> <name>Master Password CLI</name>
<description>A CLI interface to the Master Password algorithm</description> <description>A CLI interface to the Master Password algorithm</description>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-cli</artifactId> <artifactId>masterpassword-cli</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -37,7 +36,7 @@
<phase>prepare-package</phase> <phase>prepare-package</phase>
<configuration> <configuration>
<target> <target>
<chmod file="${project.build.directory}/install" perm="755"/> <chmod file="${project.build.directory}/install" perm="755" />
</target> </target>
</configuration> </configuration>
<goals> <goals>

View File

@ -45,7 +45,7 @@ public class CLI {
throws IOException { throws IOException {
// Read information from the environment. // Read information from the environment.
char[] masterPassword = null; char[] masterPassword;
String siteName = null, context = null; String siteName = null, context = null;
String userName = System.getenv( ENV_USERNAME ); String userName = System.getenv( ENV_USERNAME );
String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" ); String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" );

View File

@ -13,7 +13,6 @@
<name>Master Password GUI</name> <name>Master Password GUI</name>
<description>A GUI interface to the Master Password algorithm</description> <description>A GUI interface to the Master Password algorithm</description>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-gui</artifactId> <artifactId>masterpassword-gui</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -67,6 +66,67 @@
</plugins> </plugins>
</build> </build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
<configuration>
<sign>
<debug>false</debug>
</sign>
</configuration>
<executions>
<execution>
<id>manifest-update</id>
<phase>process-resources</phase>
<goals>
<goal>manifest-update</goal>
</goals>
<configuration>
<manifestVersionCodeUpdateFromVersion>true</manifestVersionCodeUpdateFromVersion>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jarsigner-plugin</artifactId>
<executions>
<execution>
<id>signing</id>
<goals>
<goal>sign</goal>
</goals>
<phase>package</phase>
<inherited>true</inherited>
<configuration>
<archiveDirectory />
<includes>
<include>target/*.jar</include>
</includes>
<keystore>release.jks</keystore>
<storepass>${env.PASSWORD}</storepass>
<keypass>${env.PASSWORD}</keypass>
<alias>masterpassword-desktop</alias>
<arguments>
<argument>-sigalg</argument><argument>MD5withRSA</argument>
<argument>-digestalg</argument><argument>SHA1</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<!-- DEPENDENCY MANAGEMENT --> <!-- DEPENDENCY MANAGEMENT -->
<dependencies> <dependencies>

View File

@ -0,0 +1 @@
/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks

View File

@ -21,8 +21,7 @@ import com.google.common.base.Charsets;
import com.google.common.io.*; import com.google.common.io.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.TypeUtils; import com.lyndir.lhunath.opal.system.util.TypeUtils;
import com.lyndir.masterpassword.MasterKey;
import com.lyndir.masterpassword.model.IncorrectMasterPasswordException;
import java.io.*; import java.io.*;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
@ -41,7 +40,7 @@ public class GUI implements UnlockFrame.SignInCallback {
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( GUI.class ); 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; private PasswordFrame passwordFrame;
public static void main(final String[] args) public static void main(final String[] args)
@ -50,7 +49,13 @@ public class GUI implements UnlockFrame.SignInCallback {
if (Config.get().checkForUpdates()) if (Config.get().checkForUpdates())
checkUpdate(); checkUpdate();
TypeUtils.<GUI>newInstance( AppleGUI.class ).or( new GUI() ).open(); try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException ignored) {
}
TypeUtils.<GUI>newInstance( "com.lyndir.masterpassword.gui.platform.mac.AppleGUI" ).or( new GUI() ).open();
} }
private static void checkUpdate() { private static void checkUpdate() {
@ -82,7 +87,7 @@ public class GUI implements UnlockFrame.SignInCallback {
} }
} }
void open() { protected void open() {
SwingUtilities.invokeLater( new Runnable() { SwingUtilities.invokeLater( new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -1,6 +1,7 @@
package com.lyndir.masterpassword.gui; package com.lyndir.masterpassword.gui;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.*; import com.google.common.collect.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.model.MPUser; import com.lyndir.masterpassword.model.MPUser;
@ -12,6 +13,7 @@ import javax.annotation.Nullable;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; 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.setFont( Res.valueFont().deriveFont( 12f ) );
userField.addItemListener( this ); userField.addItemListener( this );
userField.addActionListener( 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( userField );
add( Components.stud() ); add( Components.stud() );
@ -137,8 +148,8 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite
return FluentIterable.from( MPUserFileManager.get().getUsers() ).transform( new Function<MPUser, ModelUser>() { return FluentIterable.from( MPUserFileManager.get().getUsers() ).transform( new Function<MPUser, ModelUser>() {
@Nullable @Nullable
@Override @Override
public ModelUser apply(final MPUser model) { public ModelUser apply(@Nullable final MPUser model) {
return new ModelUser( model ); return new ModelUser( Preconditions.checkNotNull( model ) );
} }
} ).toArray( ModelUser.class ); } ).toArray( ModelUser.class );
} }

View File

@ -48,7 +48,7 @@ public class ModelUser extends User {
public void authenticate(final char[] masterPassword) public void authenticate(final char[] masterPassword)
throws IncorrectMasterPasswordException { throws IncorrectMasterPasswordException {
model.authenticate( masterPassword ); putKey( model.authenticate( masterPassword ) );
this.masterPassword = masterPassword; this.masterPassword = masterPassword;
} }
@ -81,6 +81,7 @@ public class ModelUser extends User {
} }
public boolean keySaved() { public boolean keySaved() {
// TODO
return false; return false;
} }
} }

View File

@ -149,11 +149,12 @@ public class PasswordFrame extends JFrame implements DocumentListener {
} ); } );
// Password // Password
passwordField = new JPasswordField(); passwordField = Components.passwordField();
passwordField.setAlignmentX( Component.CENTER_ALIGNMENT ); passwordField.setAlignmentX(Component.CENTER_ALIGNMENT);
passwordField.setEditable( false ); passwordField.setHorizontalAlignment(JTextField.CENTER);
passwordField.setHorizontalAlignment( JTextField.CENTER ); passwordField.putClientProperty("JPasswordField.cutCopyAllowed", true);
passwordField.putClientProperty( "JPasswordField.cutCopyAllowed", true ); passwordField.setEditable(false);
passwordField.setBackground(null);
passwordField.setBorder( null ); passwordField.setBorder( null );
passwordEchoChar = passwordField.getEchoChar(); passwordEchoChar = passwordField.getEchoChar();
passwordEchoFont = passwordField.getFont().deriveFont( 40f ); passwordEchoFont = passwordField.getFont().deriveFont( 40f );

Some files were not shown because too many files have changed in this diff Show More