Compare commits
56 Commits
2.1-cli2
...
2.2-androi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73372f507a | ||
|
|
feddd038e3 | ||
|
|
3f6773f3a9 | ||
|
|
12b1610dc7 | ||
|
|
e20b33a051 | ||
|
|
b84ae532f2 | ||
|
|
145008406d | ||
|
|
a6ab9b9194 | ||
|
|
78c593fc08 | ||
|
|
5b08149ca6 | ||
|
|
58afc19c26 | ||
|
|
b3109187e9 | ||
|
|
a6e7a749bf | ||
|
|
ca5d83d40c | ||
|
|
285813324f | ||
|
|
d4b772b467 | ||
|
|
f392ad4053 | ||
|
|
35990f3bdd | ||
|
|
3932857c21 | ||
|
|
6f771a972b | ||
|
|
806a07135a | ||
|
|
f6b2287778 | ||
|
|
f4e90bb839 | ||
|
|
21630e919b | ||
|
|
ae74ab6906 | ||
|
|
caf361cd10 | ||
|
|
aeedc1946e | ||
|
|
93ae31f679 | ||
|
|
d5ff215da2 | ||
|
|
b34f7377da | ||
|
|
0c2e182039 | ||
|
|
438daf27ee | ||
|
|
aa6634970a | ||
|
|
9052416786 | ||
|
|
9d19eaf667 | ||
|
|
7ae9afa63a | ||
|
|
3d856b3773 | ||
|
|
7617b2382a | ||
|
|
a03dcf6859 | ||
|
|
57769ba199 | ||
|
|
6304b3a619 | ||
|
|
d1649f3c33 | ||
|
|
80f507b4cc | ||
|
|
f8a665db65 | ||
|
|
b15f2a8a26 | ||
|
|
e9094097a2 | ||
|
|
bea6ac5e68 | ||
|
|
778533ac7f | ||
|
|
83fcde5bd0 | ||
|
|
c9ec5874d3 | ||
|
|
4ce5fd25bc | ||
|
|
1ed28ebc9b | ||
|
|
c03199f7e5 | ||
|
|
9f10bcdec4 | ||
|
|
82c96ddfe3 | ||
|
|
c0fea076b9 |
3
.gitignore
vendored
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
125
MasterPassword/C/mpw-algorithm_v0.c
Normal 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;
|
||||
}
|
||||
109
MasterPassword/C/mpw-algorithm_v1.c
Normal 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;
|
||||
}
|
||||
109
MasterPassword/C/mpw-algorithm_v2.c
Normal 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;
|
||||
}
|
||||
109
MasterPassword/C/mpw-algorithm_v3.c
Normal 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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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." );
|
||||
|
||||
@@ -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." );
|
||||
|
||||
@@ -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 )];
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
|
||||
@@ -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
@@ -0,0 +1,2 @@
|
||||
target
|
||||
dependency-reduced-pom.xml
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
*
|
||||
* @author lhunath, 15-02-04
|
||||
*/
|
||||
|
||||
|
||||
@ParametersAreNonnullByDefault package com.lyndir.masterpassword;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() );
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# File used by Eclipse to determine the target system
|
||||
# Project target.
|
||||
target=android-16
|
||||
@@ -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>
|
||||
|
||||
1
MasterPassword/Java/masterpassword-android/release.jks
Symbolic link
@@ -0,0 +1 @@
|
||||
/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks
|
||||
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 292 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 49 KiB |
@@ -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>
|
||||
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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" />
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="MPTheme" parent="android:Theme.Holo.Dialog.MinWidth">
|
||||
</style>
|
||||
</resources>
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
*
|
||||
* @author lhunath, 15-02-04
|
||||
*/
|
||||
|
||||
|
||||
@ParametersAreNonnullByDefault
|
||||
package com.lyndir.masterpassword;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -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 -->
|
||||
|
||||