2
0

Compare commits

...

56 Commits

Author SHA1 Message Date
Maarten Billemont
73372f507a [maven-release-plugin] prepare release 2.2-android 2015-02-05 20:28:25 -05:00
Maarten Billemont
feddd038e3 Also sign masterpassword-gui 2015-02-05 20:27:09 -05:00
Maarten Billemont
3f6773f3a9 Android release preparation. 2015-02-05 17:29:29 -05:00
Maarten Billemont
12b1610dc7 Remove unavailable resources + update graphics. 2015-02-05 16:34:04 -05:00
Maarten Billemont
e20b33a051 Prepare for making Android releases. 2015-02-05 13:43:57 -05:00
Maarten Billemont
b84ae532f2 Some generic code fix-ups throughout. 2015-02-05 13:14:17 -05:00
Maarten Billemont
145008406d Big overhaul for proper site-specific algorithm support and big Android UI update. 2015-02-05 00:56:24 -05:00
Maarten Billemont
a6ab9b9194 Moar UI work on the Java app + support for per-site algorithm versioning. 2015-02-04 19:51:38 -05:00
Maarten Billemont
78c593fc08 Many UI improvements to the Java GUI. 2015-02-04 11:25:18 -05:00
Maarten Billemont
5b08149ca6 Fix a crash on older OS X versions. 2015-02-02 14:48:12 -05:00
Maarten Billemont
58afc19c26 Make string constants C constants, not macros. 2015-02-02 14:47:16 -05:00
Maarten Billemont
b3109187e9 Some project cleanup. 2015-01-31 11:08:22 -05:00
Maarten Billemont
a6e7a749bf Android improvements.
[UPDATED]   Opal API
[ADDED]     Scrypt native binaries for more archs.
[IMPROVED]  Android activity secure.
[FIXED]     White background on buttons for some devices.
[IMPROVED]  Android layout.
[WIP]       Remember password on Android.
2015-01-31 10:55:08 -05:00
Maarten Billemont
ca5d83d40c Put LDFLAGS after .o files that use them for compatibility with the 11.10+ linker. 2015-01-29 14:37:15 -05:00
Maarten Billemont
285813324f mpw-js is now back in sync with Tom's repo. 2015-01-23 08:31:50 -05:00
Maarten Billemont
d4b772b467 Update to Tom's latest mpw-js to fix issues with maximum security passwords. 2015-01-23 08:14:13 -05:00
Maarten Billemont
f392ad4053 Merge branch 'master' of github.com:Lyndir/MasterPassword 2015-01-21 11:12:23 -05:00
Maarten Billemont
35990f3bdd Update Java GUI. 2015-01-21 09:11:14 -05:00
Maarten Billemont
3932857c21 Fix for not being able to copy the password from the password field. 2015-01-21 09:07:57 -05:00
Maarten Billemont
6f771a972b his -> their. 2015-01-20 10:37:43 -05:00
Maarten Billemont
806a07135a Update 2.1-cli4 again. 2015-01-19 23:32:26 -05:00
Maarten Billemont
f6b2287778 Build fixes for iOS and Mac. 2015-01-19 23:30:19 -05:00
Maarten Billemont
f4e90bb839 Update 2.1-cli4 2015-01-19 23:23:10 -05:00
Maarten Billemont
21630e919b Standardize includes for other POSIX platforms. 2015-01-19 23:21:10 -05:00
Maarten Billemont
ae74ab6906 Site references to mpw-cli C packages. 2015-01-19 23:07:49 -05:00
Maarten Billemont
caf361cd10 Rename mpw formula. 2015-01-19 21:54:50 -05:00
Maarten Billemont
aeedc1946e Some audit fixes to mpw.rb. 2015-01-19 21:53:41 -05:00
Maarten Billemont
93ae31f679 An initial Homebrew formula for installing mpw. 2015-01-19 21:48:44 -05:00
Maarten Billemont
d5ff215da2 Support for passing the master password non-interactively for testing. 2015-01-19 21:34:54 -05:00
Maarten Billemont
b34f7377da Handle dependencies unpacked by a package manager. 2015-01-19 20:58:53 -05:00
Maarten Billemont
0c2e182039 Release a new C CLI and update links. 2015-01-19 17:22:05 -05:00
Maarten Billemont
438daf27ee Use anchor tags for anchors to avoid id collision. 2015-01-19 00:23:27 -05:00
Maarten Billemont
aa6634970a Fix anchors. 2015-01-19 00:17:14 -05:00
Maarten Billemont
9052416786 Update Java Desktop app. 2015-01-19 00:15:33 -05:00
Maarten Billemont
9d19eaf667 Some anchors. 2015-01-19 00:13:28 -05:00
Maarten Billemont
7ae9afa63a Merge commit '3d856b3' 2015-01-17 13:51:31 -05:00
Maarten Billemont
3d856b3773 Warnings update. 2015-01-17 13:51:22 -05:00
Maarten Billemont
7617b2382a Fix V0 C implementation. 2015-01-17 11:17:16 -05:00
Maarten Billemont
a03dcf6859 Ability to pass the algorithm version on the CLI. 2015-01-16 00:25:18 -05:00
Maarten Billemont
57769ba199 Algorithm versions in C and wire ObjC into C, remove ObjC algorithm implementation. 2015-01-15 17:43:41 -05:00
Maarten Billemont
6304b3a619 Looks like the default close operation is hide and WINDOW_CLOSING is only an interactive request to close the window. 2015-01-04 11:28:30 -05:00
Maarten Billemont
d1649f3c33 Just dispose on close and scope executors to a window, clean up on window hide. 2015-01-03 14:25:20 -05:00
Maarten Billemont
80f507b4cc Remove VERSION from project. 2015-01-02 13:41:29 -05:00
Maarten Billemont
f8a665db65 use libscryptenc-ios-sim for simulator builds. 2015-01-02 12:51:23 -05:00
Maarten Billemont
b15f2a8a26 Properly invoke the default close operation when dismissing the password frame. 2015-01-02 12:19:49 -05:00
Maarten Billemont
e9094097a2 Mask the generated password by default, provide a check box to unmask it. 2014-12-31 14:46:44 -05:00
Maarten Billemont
bea6ac5e68 Attempt to fix copy issue when Java app closes after copying. 2014-12-31 14:04:14 -05:00
Maarten Billemont
778533ac7f Fix log-in after entering the wrong master password. 2014-12-31 13:53:28 -05:00
Maarten Billemont
83fcde5bd0 Add new Mac OS X binary. 2014-12-31 13:44:02 -05:00
Maarten Billemont
c9ec5874d3 Add support for Crashlytics to Mac OS X app. 2014-12-31 13:02:23 -05:00
Maarten Billemont
4ce5fd25bc Allow importing without a KeyID, fix a possible deadlock and fix showing error messages + replace light font with regular on non-retina. 2014-12-29 16:37:58 -05:00
Maarten Billemont
1ed28ebc9b Update Master Password for Java GUI. 2014-12-29 16:32:48 -05:00
Maarten Billemont
c03199f7e5 Update directory to mpw.d and fix issue that caused only one user to be visible in the drop-down. 2014-12-28 14:46:20 -05:00
Maarten Billemont
9f10bcdec4 Bump cli2 to fix symlink issue. 2014-12-21 23:59:07 -05:00
Maarten Billemont
82c96ddfe3 Update distribute script to include source files for symlinks. 2014-12-21 23:58:33 -05:00
Maarten Billemont
c0fea076b9 Release 2.1-cli2 2014-12-21 23:50:41 -05:00
192 changed files with 2991 additions and 1510 deletions

3
.gitignore vendored
View File

@@ -28,9 +28,6 @@ Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
/sendipa/*
!/sendipa/sendipa.conf
# Java
MasterPassword/Java/**/target
# C
MasterPassword/C/VERSION
MasterPassword/C/*.o

2
.gitmodules vendored
View File

@@ -18,4 +18,4 @@
url = git://github.com/jonmarimba/jrswizzle.git
[submodule "Site/mpw-js/js/mpw-js"]
path = Site/mpw-js/js/mpw-js
url = https://github.com/Lyndir/mpw-js.git
url = https://github.com/tmthrgd/mpw-js.git

2
External/Pearl vendored

View File

@@ -81,11 +81,15 @@ unpack() {
mv "$files"/* .
rmdir "$files"
fi
touch .unpacked
}
fetchSource() (
source .source
if [[ $pkg && -e "${pkg##*/}" ]]; then
if [[ -e .unpacked ]]; then
true
elif [[ $pkg && -e "${pkg##*/}" ]]; then
files=( !("${pkg##*/}") )
[[ -e $files ]] || {
echo
@@ -135,11 +139,14 @@ fetchSource() (
exit 1
fi
for patch in "${patches[@]}"; do
echo
echo "Patching: ${PWD##*/}, for $patch..."
patch -p0 < ../"${PWD##*/}-$patch.patch"
done
if [[ ! -e .patched ]] && (( ${#patches[@]} )); then
for patch in "${patches[@]}"; do
echo
echo "Patching: ${PWD##*/}, for $patch..."
patch -p0 < ../"${PWD##*/}-$patch.patch"
done
touch .patched
fi
)
depend() {
@@ -220,8 +227,8 @@ mpw() {
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[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
mpw-cli.c -o mpw
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
"${LDFLAGS[@]}" "mpw-cli.c" -o "mpw"
echo "done! Now run ./install or use ./mpw"
}
@@ -257,8 +264,8 @@ mpw-bench() {
-l"crypto"
)
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
mpw-bench.c -o mpw-bench
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
echo "done! Now use ./mpw-bench"
}
@@ -273,6 +280,7 @@ mpw-tests() {
# include paths
-I"lib/include"
-I"/usr/include/libxml2"
-I"/usr/local/include/libxml2"
)
LDFLAGS=(
# scrypt
@@ -288,9 +296,9 @@ mpw-tests() {
-l"crypto" -l"xml2"
)
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
mpw-tests.c -o mpw-tests
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"
}

View File

@@ -4,14 +4,14 @@ set -e
cd "${BASH_SOURCE%/*}"
tag=$(git describe)
commit=$(git describe --long --dirty)
[[ $tag && $commit = $tag-* ]] || exit 1
[[ $tag && $commit = $tag* ]] || exit 1
git show --show-signature --pretty=format:%H --quiet "$tag" > VERSION
mpwArchive=mpw-$commit.tar.gz
[[ -e $mpwArchive ]] && echo "WARNING: $mpwArchive already exists. Will overwrite."
read -n1 -p "Will prepare and release $mpwArchive. Press a key to continue or ^C to abort."
git ls-files -z . | xargs -0 tar -cvzf "$mpwArchive"
git ls-files -z . | xargs -0 tar -Lcvzf "$mpwArchive"
echo "$mpwArchive ready, SHA256: $(openssl sha -sha256 < "$mpwArchive")"
cd ../../Site/current

View File

@@ -6,106 +6,48 @@
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "mpw-types.h"
#include "mpw-util.h"
#include "mpw-algorithm.h"
#include "mpw-algorithm_v0.c"
#include "mpw-algorithm_v1.c"
#include "mpw-algorithm_v2.c"
#include "mpw-algorithm_v3.c"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword) {
const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
trc( "fullName: %s\n", fullName );
trc( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_masterKeyForUser_v0( fullName, masterPassword );
case MPAlgorithmVersion1:
return mpw_masterKeyForUser_v1( fullName, masterPassword );
case MPAlgorithmVersion2:
return mpw_masterKeyForUser_v2( fullName, masterPassword );
case MPAlgorithmVersion3:
return mpw_masterKeyForUser_v3( fullName, masterPassword );
default:
ftl( "Unsupported version: %d", algorithmVersion );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
const char *mpw_passwordForSite(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
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? "<empty>": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_passwordForSite_v0( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
case MPAlgorithmVersion1:
return mpw_passwordForSite_v1( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
case MPAlgorithmVersion2:
return mpw_passwordForSite_v2( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
case MPAlgorithmVersion3:
return mpw_passwordForSite_v3( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
default:
ftl( "Unsupported version: %d", algorithmVersion );
return NULL;
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@@ -6,15 +6,27 @@
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#define MP_dkLen 64
#include "mpw-types.h"
typedef enum(unsigned int, MPAlgorithmVersion) {
/** V0 did math with chars whose signedness was platform-dependent. */
MPAlgorithmVersion0,
/** V1 miscounted the byte-length of multi-byte site names. */
MPAlgorithmVersion1,
/** V2 miscounted the byte-length of multi-byte user names. */
MPAlgorithmVersion2,
/** V3 is the current version. */
MPAlgorithmVersion3,
};
#define MPAlgorithmVersionCurrent MPAlgorithmVersion3
/** Derive the master key for a user based on their name and master password.
* @return A new MP_dkLen-byte allocated buffer or NULL if an allocation error occurred. */
* @return A new MP_dkLen-byte allocated buffer or NULL if an allocation error occurred. */
const uint8_t *mpw_masterKeyForUser(
const char *fullName, const char *masterPassword);
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
/** Encode a password for the site from the given master key and site parameters.
* @return A newly allocated string or NULL if an allocation error occurred. */
* @return A newly allocated string or NULL if an allocation error occurred. */
const char *mpw_passwordForSite(
const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext);
const MPSiteVariant siteVariant, const char *siteContext, const MPAlgorithmVersion algorithmVersion);

View File

@@ -0,0 +1,125 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
static const char *mpw_templateForType_v0(MPSiteType type, uint16_t seedByte) {
size_t count = 0;
const char **templates = mpw_templatesForType( type, &count );
if (!count)
return NULL;
return templates[seedByte % count];
}
static const char mpw_characterFromClass_v0(char characterClass, uint16_t seedByte) {
const char *classCharacters = mpw_charactersInClass( characterClass );
return classCharacters[seedByte % strlen( classCharacters )];
}
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( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
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? "<empty>": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const char *sitePasswordSeed = (const char *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType_v0( siteType, htons( sitePasswordSeed[0] ) );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass_v0( template[c], htons( sitePasswordSeed[c + 1] ) );
trc( "class %c, index %u (0x%02X) -> character: %c\n",
template[c], htons( sitePasswordSeed[c + 1] ), htons( sitePasswordSeed[c + 1] ), sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@@ -0,0 +1,109 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
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( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
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? "<empty>": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@@ -0,0 +1,109 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
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( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
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? "<empty>": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@@ -0,0 +1,109 @@
//
// mpw-algorithm.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
#define MP_hash PearlHashSHA256
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( "masterPassword: %s\n", masterPassword );
trc( "key scope: %s\n", mpKeyScope );
// Calculate the master key salt.
// masterKeySalt = mpKeyScope . #fullName . fullName
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) );
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
ftl( "Could not allocate master key salt: %d\n", errno );
return NULL;
}
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
// masterKey = scrypt( masterPassword, masterKeySalt )
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( masterKeySalt, masterKeySaltSize );
if (!masterKey) {
ftl( "Could not allocate master key: %d\n", errno );
return NULL;
}
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
return masterKey;
}
static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
const MPSiteVariant siteVariant, const char *siteContext) {
const char *siteScope = mpw_scopeForVariant( siteVariant );
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? "<empty>": siteContext );
// Calculate the site seed.
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
size_t sitePasswordInfoSize = 0;
uint8_t *sitePasswordInfo = NULL;
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
if (siteContext) {
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
}
if (!sitePasswordInfo) {
ftl( "Could not allocate site seed info: %d\n", errno );
return NULL;
}
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
if (!sitePasswordSeed) {
ftl( "Could not allocate site seed: %d\n", errno );
return NULL;
}
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
// Determine the template.
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
trc( "type %d, template: %s\n", siteType, template );
if (strlen( template ) > 32) {
ftl( "Template too long for password seed: %lu", strlen( template ) );
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return NULL;
}
// Encode the password from the seed using the template.
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
for (size_t c = 0; c < strlen( template ); ++c) {
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
sitePassword[c] );
}
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
return sitePassword;
}

View File

@@ -15,7 +15,6 @@
#include <scrypt/sha256.h>
#include <bcrypt/ow-crypt.h>
#include "mpw-types.h"
#include "mpw-algorithm.h"
#include "mpw-util.h"
@@ -62,10 +61,12 @@ int main(int argc, char *const argv[]) {
unsigned int iterations = 100;
mpw_getTime( &startTime );
for (int i = 0; i < iterations; ++i) {
const uint8_t *masterKey = mpw_masterKeyForUser( fullName, masterPassword );
const uint8_t *masterKey = mpw_masterKeyForUser(
fullName, masterPassword, MPAlgorithmVersionCurrent );
if (!masterKey)
ftl( "Could not allocate master key: %d\n", errno );
free( (void *)mpw_passwordForSite( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext ) );
free( (void *)mpw_passwordForSite(
masterKey, siteName, siteType, siteCounter, siteVariant, siteContext, MPAlgorithmVersionCurrent ) );
free( (void *)masterKey );
if (i % 1 == 0)

View File

@@ -15,13 +15,13 @@
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
#include "mpw-types.h"
#include "mpw-algorithm.h"
#include "mpw-util.h"
#define MP_env_fullname "MP_FULLNAME"
#define MP_env_sitetype "MP_SITETYPE"
#define MP_env_sitecounter "MP_SITECOUNTER"
#define MP_env_algorithm "MP_ALGORITHM"
static void usage() {
@@ -39,7 +39,9 @@ static void usage() {
" n, name | 9 letter name.\n"
" p, phrase | 20 character sentence.\n\n", MP_env_sitetype );
fprintf( stderr, " -c counter The value of the counter.\n"
" Defaults to %s in env or '1'.\n\n", MP_env_sitecounter );
" Defaults to %s in env or 1.\n\n", MP_env_sitecounter );
fprintf( stderr, " -V version The algorithm version to use.\n"
" Defaults to %s in env or %d.\n\n", MP_env_algorithm, MPAlgorithmVersionCurrent );
fprintf( stderr, " -v variant The kind of content to generate.\n"
" Defaults to 'password'.\n"
" p, password | The password to log in with.\n"
@@ -102,13 +104,23 @@ int main(int argc, char *const argv[]) {
const char *siteContextString = NULL;
uint32_t siteCounter = 1;
const char *siteCounterString = getenv( MP_env_sitecounter );
MPAlgorithmVersion algorithmVersion = MPAlgorithmVersionCurrent;
const char *algorithmVersionString = getenv( MP_env_algorithm );
if (algorithmVersionString && strlen( algorithmVersionString ))
if (sscanf( algorithmVersionString, "%u", &algorithmVersion ) != 1)
ftl( "Invalid %s: %s\n", MP_env_algorithm, algorithmVersionString );
// Read the options.
for (int opt; (opt = getopt( argc, argv, "u:t:c:v:C:h" )) != -1;)
for (int opt; (opt = getopt( argc, argv, "u:P:t:c:v:V:C:h" )) != -1;)
switch (opt) {
case 'u':
fullName = optarg;
break;
case 'P':
// Do not use this. Passing your master password via the command-line
// is insecure. This is here for non-interactive testing purposes only.
masterPassword = strcpy( malloc( strlen( optarg ) + 1 ), optarg );
break;
case 't':
siteTypeString = optarg;
break;
@@ -118,6 +130,10 @@ int main(int argc, char *const argv[]) {
case 'v':
siteVariantString = optarg;
break;
case 'V':
if (sscanf( optarg, "%u", &algorithmVersion ) != 1)
ftl( "Not a version: %s\n", optarg );
break;
case 'C':
siteContextString = optarg;
break;
@@ -161,6 +177,7 @@ int main(int argc, char *const argv[]) {
siteType = MPSiteTypeGeneratedPhrase;
if (siteTypeString)
siteType = mpw_typeWithName( siteTypeString );
trc( "algorithmVersion: %u\n", algorithmVersion );
// Read the master password.
char *mpwConfigPath = homedir( ".mpw" );
@@ -188,12 +205,14 @@ int main(int argc, char *const argv[]) {
fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, mpw_identicon( fullName, masterPassword ) );
// Output the password.
const uint8_t *masterKey = mpw_masterKeyForUser( fullName, masterPassword );
const uint8_t *masterKey = mpw_masterKeyForUser(
fullName, masterPassword, algorithmVersion );
mpw_freeString( masterPassword );
if (!masterKey)
ftl( "Couldn't derive master key." );
const char *sitePassword = mpw_passwordForSite( masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString );
const char *sitePassword = mpw_passwordForSite(
masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString, algorithmVersion );
mpw_free( masterKey, MP_dkLen );
if (!sitePassword)
ftl( "Couldn't derive site password." );

View File

@@ -5,7 +5,6 @@
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
#include "mpw-types.h"
#include "mpw-algorithm.h"
#include "mpw-util.h"
@@ -40,13 +39,13 @@ int main(int argc, char *const argv[]) {
// 1. calculate the master key.
const uint8_t *masterKey = mpw_masterKeyForUser(
(char *)fullName, (char *)masterPassword );
(char *)fullName, (char *)masterPassword, MPAlgorithmVersionCurrent );
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 );
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, MPAlgorithmVersionCurrent );
mpw_free( masterKey, MP_dkLen );
if (!sitePassword)
ftl( "Couldn't derive site password." );

View File

@@ -51,56 +51,71 @@ const MPSiteType mpw_typeWithName(const char *typeName) {
abort();
}
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
inline const char **mpw_templatesForType(MPSiteType type, size_t *count) {
if (!(type & MPSiteTypeClassGenerated)) {
fprintf( stderr, "Not a generated type: %d", type );
abort();
ftl( "Not a generated type: %d", type );
*count = 0;
return NULL;
}
switch (type) {
case MPSiteTypeGeneratedMaximum: {
const char *templates[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
return templates[seedByte % 2];
*count = 2;
return (const char *[]){ "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
}
case MPSiteTypeGeneratedLong: {
const char *templates[] = { "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
*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 templates[seedByte % 21];
}
case MPSiteTypeGeneratedMedium: {
const char *templates[] = { "CvcnoCvc", "CvcCvcno" };
return templates[seedByte % 2];
*count = 2;
return (const char *[]){ "CvcnoCvc", "CvcCvcno" };
}
case MPSiteTypeGeneratedBasic: {
const char *templates[] = { "aaanaaan", "aannaaan", "aaannaaa" };
return templates[seedByte % 3];
*count = 3;
return (const char *[]){ "aaanaaan", "aannaaan", "aaannaaa" };
}
case MPSiteTypeGeneratedShort: {
return "Cvcn";
*count = 1;
return (const char *[]){"Cvcn"};
}
case MPSiteTypeGeneratedPIN: {
return "nnnn";
*count = 1;
return (const char *[]){ "nnnn" };
}
case MPSiteTypeGeneratedName: {
return "cvccvcvcv";
*count = 1;
return (const char *[]) {"cvccvcvcv"};
}
case MPSiteTypeGeneratedPhrase: {
const char *templates[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
return templates[seedByte % 3];
*count = 3;
return (const char *[]){ "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
}
default: {
fprintf( stderr, "Unknown generated type: %d", type );
abort();
ftl( "Unknown generated type: %d", type );
*count = 0;
return NULL;
}
}
}
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
size_t count = 0;
const char **templates = mpw_templatesForType( type, &count );
if (!count)
return NULL;
return templates[seedByte % count];
}
const MPSiteVariant mpw_variantWithName(const char *variantName) {
char stdVariantName[strlen( variantName )];
@@ -138,55 +153,38 @@ const char *mpw_scopeForVariant(MPSiteVariant variant) {
}
}
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
const char *mpw_charactersInClass(char characterClass) {
const char *classCharacters;
switch (characterClass) {
case 'V': {
classCharacters = "AEIOU";
break;
}
case 'C': {
classCharacters = "BCDFGHJKLMNPQRSTVWXYZ";
break;
}
case 'v': {
classCharacters = "aeiou";
break;
}
case 'c': {
classCharacters = "bcdfghjklmnpqrstvwxyz";
break;
}
case 'A': {
classCharacters = "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
break;
}
case 'a': {
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
break;
}
case 'n': {
classCharacters = "0123456789";
break;
}
case 'o': {
classCharacters = "@&%?,=[]_:-+*$#!'^~;()/.";
break;
}
case 'x': {
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
break;
}
case ' ': {
classCharacters = " ";
break;
}
case 'V':
return "AEIOU";
case 'C':
return "BCDFGHJKLMNPQRSTVWXYZ";
case 'v':
return "aeiou";
case 'c':
return "bcdfghjklmnpqrstvwxyz";
case 'A':
return "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
case 'a':
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
case 'n':
return "0123456789";
case 'o':
return "@&%?,=[]_:-+*$#!'^~;()/.";
case 'x':
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
case ' ':
return " ";
default: {
fprintf( stderr, "Unknown character class: %c", characterClass );
abort();
}
}
}
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
const char *classCharacters = mpw_charactersInClass( characterClass );
return classCharacters[seedByte % strlen( classCharacters )];
}

View File

@@ -6,32 +6,43 @@
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdlib.h>
#include <stdint.h>
#ifdef NS_ENUM
#define enum(_type, _name) NS_ENUM(_type, _name)
#else
#define enum(_type, _name) _type _name; enum
#endif
#define MP_dkLen 64
//// Types.
typedef enum {
typedef enum( unsigned int, MPSiteVariant ) {
/** Generate the password to log in with. */
MPSiteVariantPassword,
/** Generate the login name to log in as. */
MPSiteVariantLogin,
/** Generate the answer to a security question. */
MPSiteVariantAnswer,
} MPSiteVariant;
};
typedef enum {
typedef enum( unsigned int, MPSiteTypeClass ) {
/** Generate the password. */
MPSiteTypeClassGenerated = 1 << 4,
/** Store the password. */
MPSiteTypeClassStored = 1 << 5,
} MPSiteTypeClass;
};
typedef enum {
typedef enum( unsigned int, MPSiteFeature ) {
/** Export the key-protected content data. */
MPSiteFeatureExportContent = 1 << 10,
/** Never export content. */
MPSiteFeatureDevicePrivate = 1 << 11,
} MPSiteFeature;
};
typedef enum {
typedef enum( unsigned int, MPSiteType) {
MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
@@ -43,13 +54,42 @@ typedef enum {
MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
} MPSiteType;
};
//// Type utilities.
/**
* @return The variant represented by the given name.
*/
const MPSiteVariant mpw_variantWithName(const char *variantName);
/**
* @return An internal string containing the scope identifier to apply when encoding for the given variant.
*/
const char *mpw_scopeForVariant(MPSiteVariant variant);
/**
* @return The type represented by the given name.
*/
const MPSiteType mpw_typeWithName(const char *typeName);
/**
* @return An 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.
*/
const char **mpw_templatesForType(MPSiteType type, size_t *count);
/**
* @return An internal string that contains the password encoding template of the given type
* for a seed that starts with the given byte.
*/
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte);
/**
* @return An internal string that contains all the characters that occur in the given character class.
*/
const char *mpw_charactersInClass(char characterClass);
/**
* @return A character from given character class that encodes the given byte.
*/
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);

View File

@@ -9,6 +9,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <scrypt/sha256.h>
#include <scrypt/crypto_scrypt.h>
@@ -163,3 +164,9 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
free( resetString );
return identicon;
}
const size_t mpw_charlen(const char *string) {
setlocale( LC_ALL, "en_US.UTF-8" );
return mbstowcs( NULL, string, strlen( string ) );
}

View File

@@ -6,13 +6,19 @@
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdint.h>
//// Logging.
#ifdef DEBUG
#ifndef trc
#define trc(...) fprintf( stderr, __VA_ARGS__ )
#endif
#else
#ifndef trc
#define trc(...) do {} while (0)
#endif
#endif
#ifndef ftl
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); abort(); } while (0)
#endif
@@ -50,11 +56,15 @@ uint8_t const *mpw_hmac_sha256(
//// Visualizers.
/** Encode a buffer as a string of hexadecimal characters.
* @return 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);
/** Encode a fingerprint for a buffer.
* @return 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);
/** Encode a visual fingerprint for a user.
* @return A newly allocated string. */
const char *mpw_identicon(const char *fullName, const char *masterPassword);
//// String utilities.
const size_t mpw_charlen(const char *string);

2
MasterPassword/Java/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
target
dependency-reduced-pom.xml

View File

@@ -7,13 +7,12 @@
<parent>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
<version>2.2</version>
</parent>
<name>Master Password Algorithm Implementation</name>
<description>The implementation of the Master Password algorithm</description>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-algorithm</artifactId>
<packaging>jar</packaging>
@@ -24,12 +23,12 @@
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-system</artifactId>
<version>1.6-p6</version>
<version>1.6-p8</version>
</dependency>
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-crypto</artifactId>
<version>1.6-p6</version>
<version>1.6-p8</version>
</dependency>
<!-- EXTERNAL DEPENDENCIES -->

View File

@@ -143,11 +143,11 @@ public enum MPSiteType {
}
/**
* @param name The name of the type to look up. It is matched case insensitively.
* @param name The name fromInt the type to look up. It is matched case insensitively.
*
* @return The type registered with the given name.
*/
public static MPSiteType forName(final String name) {
public static MPSiteType forName(@Nullable final String name) {
if (name == null)
return null;

View File

@@ -3,6 +3,7 @@ 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;
/**
@@ -61,11 +62,11 @@ public enum MPSiteVariant {
throw logger.bug( "No variant for option: %s", option );
}
/**
* @param name The name of the variant to look up. It is matched case insensitively.
* @param name The name fromInt the variant to look up. It is matched case insensitively.
*
* @return The variant registered with the given name.
*/
public static MPSiteVariant forName(final String name) {
public static MPSiteVariant forName(@Nullable final String name) {
if (name == null)
return null;

View File

@@ -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;
/**

View File

@@ -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;
/**

View File

@@ -1,154 +1,164 @@
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 java.util.Arrays;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
/**
* @author lhunath, 2014-08-30
*/
public class MasterKey {
public static final int ALGORITHM = 1;
public static final String VERSION = "2.1";
public abstract class MasterKey {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKey.class );
private static final int MP_N = 32768;
private static final int MP_r = 8;
private static final int MP_p = 2;
private static final int MP_dkLen = 64;
private static final int MP_intLen = 32;
private static final Charset MP_charset = Charsets.UTF_8;
private static final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
private static final MessageDigests MP_hash = MessageDigests.SHA256;
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
private static final Logger logger = Logger.get( MasterKey.class );
@Nonnull
private final String fullName;
private final byte[] masterKey;
private boolean valid;
@Nullable
private byte[] masterKey;
public MasterKey(final String fullName, final String masterPassword) {
public static MasterKey create(final String fullName, final char[] masterPassword) {
return create( Version.CURRENT, fullName, masterPassword );
}
@Nonnull
public static MasterKey create(Version version, final String fullName, final char[] masterPassword) {
switch (version) {
case V0:
return new MasterKeyV0( fullName ).revalidate( masterPassword );
case V1:
return new MasterKeyV1( fullName ).revalidate( masterPassword );
case V2:
return new MasterKeyV2( fullName ).revalidate( masterPassword );
case V3:
return new MasterKeyV3( fullName ).revalidate( masterPassword );
}
throw new UnsupportedOperationException( "Unsupported version: " + version );
}
protected MasterKey(@NotNull final String fullName) {
this.fullName = fullName;
logger.trc( "fullName: %s", fullName );
logger.trc( "masterPassword: %s", masterPassword );
long start = System.currentTimeMillis();
byte[] userNameBytes = fullName.getBytes( MP_charset );
byte[] userNameLengthBytes = bytesForInt( userNameBytes.length );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), userNameLengthBytes, userNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
try {
masterKey = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
valid = true;
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
(System.currentTimeMillis() - start) / 1000D );
}
catch (GeneralSecurityException e) {
throw logger.bug( e );
}
}
@Nullable
protected abstract byte[] deriveKey(final char[] masterPassword);
public abstract Version getAlgorithmVersion();
@NotNull
public String getFullName() {
return fullName;
}
@Nonnull
protected byte[] getKey() {
return Preconditions.checkNotNull( masterKey );
}
public byte[] getKeyID() {
Preconditions.checkState( valid );
return idForBytes( masterKey );
return idForBytes( getKey() );
}
private byte[] getSubKey(final int subkeyLength) {
public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, int siteCounter,
final MPSiteVariant siteVariant, @Nullable final String siteContext);
Preconditions.checkState( valid );
byte[] subkey = new byte[Math.min( subkeyLength, masterKey.length )];
System.arraycopy( masterKey, 0, subkey, 0, subkey.length );
return subkey;
}
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
@Nullable final String siteContext) {
Preconditions.checkState( valid );
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter == 0)
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
String siteScope = siteVariant.getScope();
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[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContext == 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( masterKey, sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
public boolean isValid() {
return masterKey != null;
}
public void invalidate() {
valid = false;
Arrays.fill( masterKey, (byte) 0 );
if (masterKey != null) {
Arrays.fill( masterKey, (byte) 0 );
masterKey = null;
}
}
private static byte[] bytesForInt(final int integer) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
public MasterKey revalidate(final char[] masterPassword) {
invalidate();
logger.trc( "masterPassword: %s", new String( masterPassword ) );
long start = System.currentTimeMillis();
masterKey = deriveKey( masterPassword );
if (masterKey == null)
logger.dbg( "masterKey calculation failed after %.2fs.", (System.currentTimeMillis() - start) / 1000D );
else
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
(System.currentTimeMillis() - start) / 1000D );
return this;
}
private static byte[] idForBytes(final byte[] bytes) {
return MP_hash.of( bytes );
protected abstract byte[] bytesForInt(final int integer);
protected abstract byte[] idForBytes(final byte[] bytes);
public enum Version {
/**
* bugs:
* - does math with chars whose signedness was platform-dependent.
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*/
V0,
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*/
V1,
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte full names.
*/
V2,
/**
* bugs:
* - no known issues.
*/
V3;
public static final Version CURRENT = V3;
public static Version fromInt(final int algorithmVersion) {
return values()[algorithmVersion];
}
public int toInt() {
return ordinal();
}
public String toBundleVersion() {
switch (this) {
case V0:
return "1.0";
case V1:
return "2.0";
case V2:
return "2.1";
case V3:
return "2.2";
}
throw new UnsupportedOperationException( "Unsupported version: " + this );
}
}
}

View File

@@ -0,0 +1,135 @@
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.*;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* bugs:
* - does math with chars whose signedness was platform-dependent.
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV0 extends MasterKey {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV0.class );
protected final int MP_N = 32768;
protected final int MP_r = 8;
protected final int MP_p = 2;
protected final int MP_dkLen = 64;
protected final int MP_intLen = 32;
protected final Charset MP_charset = Charsets.UTF_8;
protected final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
protected final MessageDigests MP_hash = MessageDigests.SHA256;
protected final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
public MasterKeyV0(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V0;
}
@Nullable
@Override
protected byte[] deriveKey(final char[] masterPassword) {
String fullName = getFullName();
byte[] fullNameBytes = fullName.getBytes( MP_charset );
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
CharBuffer mpChars = CharBuffer.wrap( masterPassword );
byte[] mpBytes = MP_charset.encode( mpChars ).array();
try {
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
}
catch (GeneralSecurityException e) {
logger.bug( e );
return null;
}
finally {
Arrays.fill( mpBytes, (byte) 0 );
}
}
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
@Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter == 0)
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
String siteScope = siteVariant.getScope();
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[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContext == 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( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFFFF;
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;
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
@Override
protected byte[] bytesForInt(final int integer) {
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
}
@Override
protected byte[] idForBytes(final byte[] bytes) {
return MP_hash.of( bytes );
}
}

View File

@@ -0,0 +1,82 @@
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.lyndir.lhunath.opal.system.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import javax.annotation.Nullable;
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte site names.
* - miscounted the byte-length fromInt multi-byte full names.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV1 extends MasterKeyV0 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV1.class );
public MasterKeyV1(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V1;
}
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
@Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter == 0)
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
String siteScope = siteVariant.getScope();
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[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContext == 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( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
}

View File

@@ -0,0 +1,81 @@
package com.lyndir.masterpassword;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Bytes;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import javax.annotation.Nullable;
/**
* bugs:
* - miscounted the byte-length fromInt multi-byte full names.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV2 extends MasterKeyV1 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV2.class );
public MasterKeyV2(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V2;
}
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
@Nullable final String siteContext) {
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
Preconditions.checkArgument( !siteName.isEmpty() );
logger.trc( "siteName: %s", siteName );
logger.trc( "siteCounter: %d", siteCounter );
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
if (siteCounter == 0)
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
String siteScope = siteVariant.getScope();
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[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
siteContext == 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( getKey(), sitePasswordInfo );
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
Preconditions.checkState( sitePasswordSeed.length > 0 );
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
StringBuilder password = new StringBuilder( template.length() );
for (int i = 0; i < template.length(); ++i) {
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
sitePasswordSeed[i + 1], passwordCharacter );
password.append( passwordCharacter );
}
return password.toString();
}
}

View File

@@ -0,0 +1,58 @@
package com.lyndir.masterpassword;
import com.google.common.primitives.Bytes;
import com.lambdaworks.crypto.SCrypt;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.nio.CharBuffer;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.annotation.Nullable;
/**
* bugs:
* - no known issues.
*
* @author lhunath, 2014-08-30
*/
public class MasterKeyV3 extends MasterKeyV2 {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKeyV3.class );
public MasterKeyV3(final String fullName) {
super( fullName );
}
@Override
public Version getAlgorithmVersion() {
return Version.V3;
}
@Nullable
@Override
protected byte[] deriveKey(final char[] masterPassword) {
byte[] fullNameBytes = getFullName().getBytes( MP_charset );
byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
String mpKeyScope = MPSiteVariant.Password.getScope();
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
logger.trc( "key scope: %s", mpKeyScope );
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
CharBuffer mpChars = CharBuffer.wrap( masterPassword );
byte[] mpBytes = MP_charset.encode( mpChars ).array();
try {
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
}
catch (GeneralSecurityException e) {
logger.bug( e );
return null;
}
finally {
Arrays.fill( mpBytes, (byte) 0 );
}
}
}

View File

@@ -0,0 +1,9 @@
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault package com.lyndir.masterpassword;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -80,7 +80,7 @@ public class MPWTests {
@Nonnull
@Override
public String get() {
return parentCase.getMasterPassword();
return new String( parentCase.getMasterPassword() );
}
} );
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
@@ -148,8 +148,8 @@ public class MPWTests {
return fullName;
}
public String getMasterPassword() {
return masterPassword;
public char[] getMasterPassword() {
return masterPassword == null? null: masterPassword.toCharArray();
}
public String getKeyID() {
@@ -161,7 +161,7 @@ public class MPWTests {
}
public int getSiteCounter() {
return siteCounter;
return ifNotNullElse( siteCounter, 1 );
}
public MPSiteType getSiteType() {

View File

@@ -35,7 +35,7 @@ public class MasterKeyTest {
throws Exception {
for (MPWTests.Case testCase : tests.getCases()) {
MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals(
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
@@ -46,7 +46,7 @@ public class MasterKeyTest {
public void testGetUserName()
throws Exception {
assertEquals( new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
defaultCase.getFullName() );
}
@@ -55,7 +55,7 @@ public class MasterKeyTest {
throws Exception {
for (MPWTests.Case testCase : tests.getCases()) {
MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
}
}
@@ -65,7 +65,7 @@ public class MasterKeyTest {
throws Exception {
try {
MasterKey masterKey = new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() );
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
masterKey.invalidate();
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );

View File

@@ -2,17 +2,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lyndir.masterpassword"
android:versionCode="1"
android:versionName="GIT-SNAPSHOT">
android:versionName="2.2">
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19" />
android:minSdkVersion="19"
android:targetSdkVersion="21" />
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:allowBackup="true">
<activity android:name=".EmergencyActivity">
<activity android:name=".EmergencyActivity" android:theme="@style/MPTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View File

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

View File

@@ -7,13 +7,12 @@
<parent>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
<version>2.2</version>
</parent>
<name>Master Password Android</name>
<description>An Android application to the Master Password algorithm</description>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-android</artifactId>
<packaging>apk</packaging>
@@ -30,7 +29,7 @@
<skip>false</skip>
</zipalign>
<sdk>
<platform>19</platform>
<platform>21</platform>
</sdk>
</configuration>
</plugin>
@@ -39,9 +38,32 @@
<profiles>
<profile>
<id>sign</id>
<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>
@@ -54,14 +76,14 @@
<phase>package</phase>
<inherited>true</inherited>
<configuration>
<archiveDirectory></archiveDirectory>
<archiveDirectory />
<includes>
<include>target/*.apk</include>
</includes>
<keystore>release.jks</keystore>
<storepass>${env.PASSWORD}</storepass>
<keypass>${env.PASSWORD}</keypass>
<alias>android</alias>
<alias>masterpassword-android</alias>
<arguments>
<argument>-sigalg</argument><argument>MD5withRSA</argument>
<argument>-digestalg</argument><argument>SHA1</argument>
@@ -70,16 +92,6 @@
</execution>
</executions>
</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>
</build>
</profile>
@@ -92,7 +104,7 @@
<dependency>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-algorithm</artifactId>
<version>GIT-SNAPSHOT</version>
<version>2.2</version>
</dependency>
<dependency>
@@ -114,11 +126,10 @@
<dependency>
<groupId>com.lambdaworks</groupId>
<artifactId>libscrypt</artifactId>
<version>1.4.0</version>
<type>so</type>
<classifier>android</classifier>
<scope>runtime</scope>
<artifactId>scrypt</artifactId>
<version>1.4.0-android</version>
<type>jar</type>
<classifier>native</classifier>
</dependency>
</dependencies>

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

@@ -8,82 +8,166 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:orientation="vertical"
android:gravity="center">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<ProgressBar
android:id="@+id/progressView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
<View
android:layout_width="0dp"
android:layout_width="1dp"
android:layout_height="0dp"
android:layout_weight="1" />
<EditText
android:id="@+id/userNameField"
android:layout_width="wrap_content"
android:id="@+id/fullNameField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/masterPasswordField"
android:inputType="text|textCapWords|textPersonName"
android:hint="@string/userName.hint"
android:hint="@string/fullName_hint"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="26sp" />
<EditText
android:id="@+id/masterPasswordField"
android:layout_width="wrap_content"
<CheckBox
android:id="@+id/rememberFullNameField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/rememberPasswordField"
android:textSize="14sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/remember" />
<EditText
android:id="@id/masterPasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteNameField"
android:inputType="text|textPassword"
android:hint="@string/masterPassword.hint"
android:password="true"
android:hint="@string/masterPassword_hint"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="18sp" />
<ImageView
android:layout_width="wrap_content"
<CheckBox
android:id="@id/rememberPasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/double_"
android:contentDescription="@string/empty" />
android:textSize="14sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/forgetOnClose" />
<EditText
android:id="@+id/siteNameField"
android:layout_width="wrap_content"
android:id="@id/siteNameField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/sitePasswordField"
android:inputType="text|textNoSuggestions|textUri"
android:hint="@string/siteName.hint"
android:hint="@string/siteName_hint"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="26sp" />
android:textSize="18sp" />
<Button
android:id="@+id/sitePasswordField"
android:layout_width="wrap_content"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progressView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="20dp"
android:indeterminate="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="@id/sitePasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteTypeField"
android:gravity="center"
android:background="@android:color/transparent"
android:textColor="#FFFFFF"
android:textSize="32sp"
android:text="LuxdZozvDuma4["
android:onClick="copySitePassword" />
<TextView
android:id="@+id/sitePasswordTip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="@id/sitePasswordField"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="14sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/sitePassword_hint" />
</LinearLayout>
</FrameLayout>
<CheckBox
android:id="@+id/maskPasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:background="@null"
android:textColor="#FFFFFF"
android:textSize="32sp"
android:text="LuxdZozvDuma4["
android:onClick="copySitePassword" />
android:textSize="14sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/maskPassword" />
<Spinner
android:id="@+id/typeField"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:id="@id/siteTypeField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/counterField"
android:gravity="center" />
<NumberPicker
android:id="@+id/counterField"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:id="@id/counterField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteVersionField"
android:gravity="center"
android:inputType="text|textNoSuggestions"
android:textColor="#FFFFFF"
android:textSize="18sp"
android:text="1" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="@id/counterField"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="14sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteCounter_hint" />
<Spinner
android:id="@id/siteVersionField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@id/rememberFullNameField"
android:gravity="center" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="@id/siteVersionField"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="14sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteVersion_hint" />
<View
android:layout_width="1dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

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="Maarten Billemont" />

View File

@@ -1,9 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Master Password</string>
<string name="avatar">User Avatar</string>
<string name="siteName.hint">Site Name</string>
<string name="userName.hint">Your Name</string>
<string name="masterPassword.hint">Your Master Password</string>
<string name="remember">Remember</string>
<string name="forgetOnClose">Forget on close</string>
<string name="maskPassword">Hide password</string>
<string name="fullName_hint">Your full name</string>
<string name="masterPassword_hint">Your master password</string>
<string name="siteName_hint">eg. google.com</string>
<string name="sitePassword_hint">Tap to copy</string>
<string name="siteCounter_hint">Password #</string>
<string name="siteVersion_hint">Algorithm</string>
<string name="empty" />
</resources>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MPTheme" parent="android:Theme.Holo.Dialog.MinWidth">
</style>
</resources>

View File

@@ -4,18 +4,23 @@ import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import android.app.Activity;
import android.content.*;
import android.content.ClipboardManager;
import android.graphics.Paint;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.*;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
import android.view.WindowManager;
import android.widget.*;
import butterknife.ButterKnife;
import butterknife.InjectView;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import java.util.Arrays;
import java.util.concurrent.*;
import javax.annotation.Nullable;
public class EmergencyActivity extends Activity {
@@ -42,8 +47,8 @@ public class EmergencyActivity extends Activity {
@InjectView(R.id.progressView)
ProgressBar progressView;
@InjectView(R.id.userNameField)
EditText userNameField;
@InjectView(R.id.fullNameField)
EditText fullNameField;
@InjectView(R.id.masterPasswordField)
EditText masterPasswordField;
@@ -51,101 +56,184 @@ public class EmergencyActivity extends Activity {
@InjectView(R.id.siteNameField)
EditText siteNameField;
@InjectView(R.id.typeField)
Spinner typeField;
@InjectView(R.id.siteTypeField)
Spinner siteTypeField;
@InjectView(R.id.counterField)
NumberPicker counterField;
EditText counterField;
@InjectView(R.id.siteVersionField)
Spinner siteVersionField;
@InjectView(R.id.sitePasswordField)
TextView sitePasswordField;
private int hc_userName;
private int hc_masterPassword;
@InjectView(R.id.sitePasswordTip)
TextView sitePasswordTip;
@InjectView(R.id.rememberFullNameField)
CheckBox rememberFullNameField;
@InjectView(R.id.rememberPasswordField)
CheckBox forgetPasswordField;
@InjectView(R.id.maskPasswordField)
CheckBox maskPasswordField;
private int hc_userName;
private int hc_masterPassword;
private String sitePassword;
@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
Res.init( getResources() );
getWindow().setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE );
setContentView( R.layout.activity_emergency );
ButterKnife.inject( this );
userNameField.setOnFocusChangeListener( updateMasterKey );
fullNameField.setOnFocusChangeListener( updateMasterKey );
masterPasswordField.setOnFocusChangeListener( updateMasterKey );
siteNameField.addTextChangedListener( updateSitePassword );
typeField.setOnItemSelectedListener( updateSitePassword );
counterField.setOnValueChangedListener( updateSitePassword );
siteTypeField.setOnItemSelectedListener( updateSitePassword );
counterField.addTextChangedListener( updateSitePassword );
siteVersionField.setOnItemSelectedListener( updateMasterKey );
sitePasswordField.addTextChangedListener( new ValueChangedListener() {
@Override
void update() {
boolean noPassword = TextUtils.isEmpty( sitePasswordField.getText() );
sitePasswordTip.setVisibility( noPassword? View.INVISIBLE: View.VISIBLE );
userNameField.setTypeface( Res.exo_Thin );
userNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
if (noPassword)
sitePassword = null;
}
} );
fullNameField.setTypeface( Res.exo_Thin );
fullNameField.setPaintFlags( fullNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
masterPasswordField.setTypeface( Res.sourceCodePro_ExtraLight );
masterPasswordField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
masterPasswordField.setPaintFlags( masterPasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
siteNameField.setTypeface( Res.exo_Regular );
siteNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
siteNameField.setPaintFlags( siteNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
sitePasswordField.setTypeface( Res.sourceCodePro_Black );
sitePasswordField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
sitePasswordField.setPaintFlags( sitePasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
typeField.setAdapter( new ArrayAdapter<>( this, R.layout.type_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) );
typeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
siteTypeField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) );
siteTypeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
counterField.setMinValue( 1 );
counterField.setMaxValue( Integer.MAX_VALUE );
counterField.setWrapSelectorWheel( false );
siteVersionField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MasterKey.Version.values() ) );
siteVersionField.setSelection( MasterKey.Version.CURRENT.ordinal() );
rememberFullNameField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
getPreferences( MODE_PRIVATE ).edit().putBoolean( "rememberFullName", isChecked ).apply();
if (isChecked)
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", fullNameField.getText().toString() ).apply();
else
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", "" ).apply();
}
} );
forgetPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
getPreferences( MODE_PRIVATE ).edit().putBoolean( "forgetPassword", isChecked ).apply();
}
} );
maskPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
getPreferences( MODE_PRIVATE ).edit().putBoolean( "maskPassword", isChecked ).apply();
sitePasswordField.setTransformationMethod( isChecked? new PasswordTransformationMethod(): null );
}
} );
}
@Override
protected void onResume() {
super.onResume();
userNameField.setText( getPreferences( MODE_PRIVATE ).getString( "userName", "" ) );
masterPasswordField.requestFocus();
fullNameField.setText( getPreferences( MODE_PRIVATE ).getString( "fullName", "" ) );
rememberFullNameField.setChecked( isRememberFullNameEnabled() );
forgetPasswordField.setChecked( isForgetPasswordEnabled() );
maskPasswordField.setChecked( isMaskPasswordEnabled() );
sitePasswordField.setTransformationMethod( isMaskPasswordEnabled()? new PasswordTransformationMethod(): null );
if (TextUtils.isEmpty( masterPasswordField.getText() ))
masterPasswordField.requestFocus();
else
siteNameField.requestFocus();
}
@Override
protected void onPause() {
synchronized (this) {
hc_userName = hc_masterPassword = 0;
if (masterKeyFuture != null) {
masterKeyFuture.cancel( true );
masterKeyFuture = null;
if (isForgetPasswordEnabled()) {
synchronized (this) {
hc_userName = hc_masterPassword = 0;
if (masterKeyFuture != null) {
masterKeyFuture.cancel( true );
masterKeyFuture = null;
}
masterPasswordField.setText( "" );
}
}
siteNameField.setText( "" );
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
super.onPause();
}
private synchronized void updateMasterKey() {
final String userName = userNameField.getText().toString();
final String masterPassword = masterPasswordField.getText().toString();
if (userName.hashCode() == hc_userName && masterPassword.hashCode() == hc_masterPassword)
return;
hc_userName = userName.hashCode();
hc_masterPassword = masterPassword.hashCode();
private boolean isRememberFullNameEnabled() {
return getPreferences( MODE_PRIVATE ).getBoolean( "rememberFullName", false );
}
SharedPreferences.Editor pref = getPreferences( MODE_PRIVATE ).edit();
pref.putString( "userName", userName );
pref.apply();
private boolean isForgetPasswordEnabled() {
return getPreferences( MODE_PRIVATE ).getBoolean( "forgetPassword", false );
}
private boolean isMaskPasswordEnabled() {
return getPreferences( MODE_PRIVATE ).getBoolean( "maskPassword", false );
}
private synchronized void updateMasterKey() {
final String fullName = fullNameField.getText().toString();
final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
final MasterKey.Version version = (MasterKey.Version) siteVersionField.getSelectedItem();
try {
if (fullName.hashCode() == hc_userName && Arrays.hashCode( masterPassword ) == hc_masterPassword &&
masterKeyFuture != null && masterKeyFuture.get().getAlgorithmVersion() == version)
return;
}
catch (InterruptedException | ExecutionException e) {
return;
}
hc_userName = fullName.hashCode();
hc_masterPassword = Arrays.hashCode( masterPassword );
if (isRememberFullNameEnabled())
getPreferences( MODE_PRIVATE ).edit().putString( "fullName", fullName ).apply();
if (masterKeyFuture != null)
masterKeyFuture.cancel( true );
if (userName.isEmpty() || masterPassword.isEmpty()) {
if (fullName.isEmpty() || masterPassword.length == 0) {
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
return;
}
sitePasswordField.setText( "" );
progressView.setVisibility( View.VISIBLE );
(masterKeyFuture = executor.submit( new Callable<MasterKey>() {
@Override
public MasterKey call()
throws Exception {
try {
return new MasterKey( userName, masterPassword );
return MasterKey.create( version, fullName, masterPassword );
}
catch (RuntimeException e) {
sitePasswordField.setText( "" );
@@ -169,21 +257,25 @@ public class EmergencyActivity extends Activity {
private void updateSitePassword() {
final String siteName = siteNameField.getText().toString();
final MPSiteType type = (MPSiteType) typeField.getSelectedItem();
final int counter = counterField.getValue();
final MPSiteType type = (MPSiteType) siteTypeField.getSelectedItem();
final int counter = ConversionUtils.toIntegerNN( counterField.getText() );
if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
if (masterKeyFuture == null)
updateMasterKey();
return;
}
sitePasswordField.setText( "" );
progressView.setVisibility( View.VISIBLE );
executor.submit( new Runnable() {
@Override
public void run() {
try {
final String sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null );
sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null );
runOnUiThread( new Runnable() {
@Override
@@ -214,19 +306,18 @@ public class EmergencyActivity extends Activity {
}
public void copySitePassword(View view) {
String sitePassword = sitePasswordField.getText().toString();
if (sitePassword.isEmpty())
if (TextUtils.isEmpty( sitePassword ))
return;
ClipDescription description = new ClipDescription( strf( "Password for %s", siteNameField.getText() ),
new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } );
((ClipboardManager) getSystemService( CLIPBOARD_SERVICE )).setPrimaryClip(
new ClipData( description, new ClipData.Item( sitePassword ) ) );
ClipData clipData = new ClipData( description, new ClipData.Item( sitePassword ) );
((ClipboardManager) getSystemService( CLIPBOARD_SERVICE )).setPrimaryClip( clipData );
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
Intent startMain = new Intent( Intent.ACTION_MAIN );
startMain.addCategory( Intent.CATEGORY_HOME );
startMain.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
startActivity( startMain );
}
private abstract class ValueChangedListener

View File

@@ -1,7 +1,6 @@
package com.lyndir.masterpassword;
import android.content.res.Resources;
import android.graphics.Paint;
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,23 +0,0 @@
package com.lyndir.masterpassword.model;
/**
* @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;
}
}

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

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

View File

@@ -45,7 +45,8 @@ public class CLI {
throws IOException {
// Read information from the environment.
String siteName = null, masterPassword, context = null;
char[] masterPassword;
String siteName = null, context = null;
String userName = System.getenv( ENV_USERNAME );
String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" );
MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
@@ -174,15 +175,15 @@ public class CLI {
}
if (console != null)
masterPassword = new String( console.readPassword( "%s's master password: ", userName ) );
masterPassword = console.readPassword( "%s's master password: ", userName );
else {
System.err.format( "%s's master password: ", userName );
masterPassword = lineReader.readLine();
masterPassword = lineReader.readLine().toCharArray();
}
}
// Encode and write out the site password.
System.out.println( new MasterKey( userName, masterPassword ).encode( siteName, siteType, siteCounter, variant, context ) );
System.out.println( MasterKey.create( userName, masterPassword ).encode( siteName, siteType, siteCounter, variant, context ) );
}
}

View File

@@ -0,0 +1,10 @@
/**
*
* @author lhunath, 15-02-04
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -7,13 +7,12 @@
<parent>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
<version>2.2</version>
</parent>
<name>Master Password GUI</name>
<description>A GUI interface to the Master Password algorithm</description>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-gui</artifactId>
<packaging>jar</packaging>
@@ -67,6 +66,67 @@
</plugins>
</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 -->
<dependencies>
@@ -74,7 +134,7 @@
<dependency>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-model</artifactId>
<version>GIT-SNAPSHOT</version>
<version>2.2</version>
</dependency>
<!-- EXTERNAL DEPENDENCIES -->

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