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/sendipa.conf
|
!/sendipa/sendipa.conf
|
||||||
|
|
||||||
# Java
|
|
||||||
MasterPassword/Java/**/target
|
|
||||||
|
|
||||||
# C
|
# C
|
||||||
MasterPassword/C/VERSION
|
MasterPassword/C/VERSION
|
||||||
MasterPassword/C/*.o
|
MasterPassword/C/*.o
|
||||||
|
|||||||
2
.gitmodules
vendored
@@ -18,4 +18,4 @@
|
|||||||
url = git://github.com/jonmarimba/jrswizzle.git
|
url = git://github.com/jonmarimba/jrswizzle.git
|
||||||
[submodule "Site/mpw-js/js/mpw-js"]
|
[submodule "Site/mpw-js/js/mpw-js"]
|
||||||
path = 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"/* .
|
mv "$files"/* .
|
||||||
rmdir "$files"
|
rmdir "$files"
|
||||||
fi
|
fi
|
||||||
|
touch .unpacked
|
||||||
}
|
}
|
||||||
fetchSource() (
|
fetchSource() (
|
||||||
source .source
|
source .source
|
||||||
|
|
||||||
if [[ $pkg && -e "${pkg##*/}" ]]; then
|
if [[ -e .unpacked ]]; then
|
||||||
|
true
|
||||||
|
|
||||||
|
elif [[ $pkg && -e "${pkg##*/}" ]]; then
|
||||||
files=( !("${pkg##*/}") )
|
files=( !("${pkg##*/}") )
|
||||||
[[ -e $files ]] || {
|
[[ -e $files ]] || {
|
||||||
echo
|
echo
|
||||||
@@ -135,11 +139,14 @@ fetchSource() (
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for patch in "${patches[@]}"; do
|
if [[ ! -e .patched ]] && (( ${#patches[@]} )); then
|
||||||
echo
|
for patch in "${patches[@]}"; do
|
||||||
echo "Patching: ${PWD##*/}, for $patch..."
|
echo
|
||||||
patch -p0 < ../"${PWD##*/}-$patch.patch"
|
echo "Patching: ${PWD##*/}, for $patch..."
|
||||||
done
|
patch -p0 < ../"${PWD##*/}-$patch.patch"
|
||||||
|
done
|
||||||
|
touch .patched
|
||||||
|
fi
|
||||||
)
|
)
|
||||||
depend() {
|
depend() {
|
||||||
|
|
||||||
@@ -220,8 +227,8 @@ mpw() {
|
|||||||
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
||||||
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
||||||
mpw-cli.c -o mpw
|
"${LDFLAGS[@]}" "mpw-cli.c" -o "mpw"
|
||||||
echo "done! Now run ./install or use ./mpw"
|
echo "done! Now run ./install or use ./mpw"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,8 +264,8 @@ mpw-bench() {
|
|||||||
-l"crypto"
|
-l"crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
||||||
mpw-bench.c -o mpw-bench
|
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
|
||||||
echo "done! Now use ./mpw-bench"
|
echo "done! Now use ./mpw-bench"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +280,7 @@ mpw-tests() {
|
|||||||
# include paths
|
# include paths
|
||||||
-I"lib/include"
|
-I"lib/include"
|
||||||
-I"/usr/include/libxml2"
|
-I"/usr/include/libxml2"
|
||||||
|
-I"/usr/local/include/libxml2"
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
LDFLAGS=(
|
||||||
# scrypt
|
# scrypt
|
||||||
@@ -288,9 +296,9 @@ mpw-tests() {
|
|||||||
-l"crypto" -l"xml2"
|
-l"crypto" -l"xml2"
|
||||||
)
|
)
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
|
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
|
||||||
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
|
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
|
||||||
mpw-tests.c -o mpw-tests
|
"${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests"
|
||||||
echo "done! Now use ./mpw-tests"
|
echo "done! Now use ./mpw-tests"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ set -e
|
|||||||
cd "${BASH_SOURCE%/*}"
|
cd "${BASH_SOURCE%/*}"
|
||||||
tag=$(git describe)
|
tag=$(git describe)
|
||||||
commit=$(git describe --long --dirty)
|
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
|
git show --show-signature --pretty=format:%H --quiet "$tag" > VERSION
|
||||||
|
|
||||||
mpwArchive=mpw-$commit.tar.gz
|
mpwArchive=mpw-$commit.tar.gz
|
||||||
[[ -e $mpwArchive ]] && echo "WARNING: $mpwArchive already exists. Will overwrite."
|
[[ -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."
|
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")"
|
echo "$mpwArchive ready, SHA256: $(openssl sha -sha256 < "$mpwArchive")"
|
||||||
|
|
||||||
cd ../../Site/current
|
cd ../../Site/current
|
||||||
|
|||||||
@@ -6,106 +6,48 @@
|
|||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
// 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.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_N 32768
|
||||||
#define MP_r 8
|
#define MP_r 8
|
||||||
#define MP_p 2
|
#define MP_p 2
|
||||||
#define MP_hash PearlHashSHA256
|
#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 );
|
switch (algorithmVersion) {
|
||||||
trc( "fullName: %s\n", fullName );
|
case MPAlgorithmVersion0:
|
||||||
trc( "masterPassword: %s\n", masterPassword );
|
return mpw_masterKeyForUser_v0( fullName, masterPassword );
|
||||||
trc( "key scope: %s\n", mpKeyScope );
|
case MPAlgorithmVersion1:
|
||||||
|
return mpw_masterKeyForUser_v1( fullName, masterPassword );
|
||||||
// Calculate the master key salt.
|
case MPAlgorithmVersion2:
|
||||||
// masterKeySalt = mpKeyScope . #fullName . fullName
|
return mpw_masterKeyForUser_v2( fullName, masterPassword );
|
||||||
size_t masterKeySaltSize = 0;
|
case MPAlgorithmVersion3:
|
||||||
uint8_t *masterKeySalt = NULL;
|
return mpw_masterKeyForUser_v3( fullName, masterPassword );
|
||||||
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
default:
|
||||||
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) );
|
ftl( "Unsupported version: %d", algorithmVersion );
|
||||||
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
|
return NULL;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *mpw_passwordForSite(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
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 );
|
switch (algorithmVersion) {
|
||||||
trc( "siteName: %s\n", siteName );
|
case MPAlgorithmVersion0:
|
||||||
trc( "siteCounter: %d\n", siteCounter );
|
return mpw_passwordForSite_v0( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||||
trc( "siteVariant: %d\n", siteVariant );
|
case MPAlgorithmVersion1:
|
||||||
trc( "siteType: %d\n", siteType );
|
return mpw_passwordForSite_v1( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||||
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
case MPAlgorithmVersion2:
|
||||||
|
return mpw_passwordForSite_v2( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||||
// Calculate the site seed.
|
case MPAlgorithmVersion3:
|
||||||
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
return mpw_passwordForSite_v3( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||||
size_t sitePasswordInfoSize = 0;
|
default:
|
||||||
uint8_t *sitePasswordInfo = NULL;
|
ftl( "Unsupported version: %d", algorithmVersion );
|
||||||
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
return NULL;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,27 @@
|
|||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
// 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.
|
/** 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 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.
|
/** 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 char *mpw_passwordForSite(
|
||||||
const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
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 <scrypt/sha256.h>
|
||||||
#include <bcrypt/ow-crypt.h>
|
#include <bcrypt/ow-crypt.h>
|
||||||
|
|
||||||
#include "mpw-types.h"
|
|
||||||
#include "mpw-algorithm.h"
|
#include "mpw-algorithm.h"
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
@@ -62,10 +61,12 @@ int main(int argc, char *const argv[]) {
|
|||||||
unsigned int iterations = 100;
|
unsigned int iterations = 100;
|
||||||
mpw_getTime( &startTime );
|
mpw_getTime( &startTime );
|
||||||
for (int i = 0; i < iterations; ++i) {
|
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)
|
if (!masterKey)
|
||||||
ftl( "Could not allocate master key: %d\n", errno );
|
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 );
|
free( (void *)masterKey );
|
||||||
|
|
||||||
if (i % 1 == 0)
|
if (i % 1 == 0)
|
||||||
|
|||||||
@@ -15,13 +15,13 @@
|
|||||||
|
|
||||||
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
|
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
|
||||||
|
|
||||||
#include "mpw-types.h"
|
|
||||||
#include "mpw-algorithm.h"
|
#include "mpw-algorithm.h"
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
#define MP_env_fullname "MP_FULLNAME"
|
#define MP_env_fullname "MP_FULLNAME"
|
||||||
#define MP_env_sitetype "MP_SITETYPE"
|
#define MP_env_sitetype "MP_SITETYPE"
|
||||||
#define MP_env_sitecounter "MP_SITECOUNTER"
|
#define MP_env_sitecounter "MP_SITECOUNTER"
|
||||||
|
#define MP_env_algorithm "MP_ALGORITHM"
|
||||||
|
|
||||||
static void usage() {
|
static void usage() {
|
||||||
|
|
||||||
@@ -39,7 +39,9 @@ static void usage() {
|
|||||||
" n, name | 9 letter name.\n"
|
" n, name | 9 letter name.\n"
|
||||||
" p, phrase | 20 character sentence.\n\n", MP_env_sitetype );
|
" p, phrase | 20 character sentence.\n\n", MP_env_sitetype );
|
||||||
fprintf( stderr, " -c counter The value of the counter.\n"
|
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"
|
fprintf( stderr, " -v variant The kind of content to generate.\n"
|
||||||
" Defaults to 'password'.\n"
|
" Defaults to 'password'.\n"
|
||||||
" p, password | The password to log in with.\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;
|
const char *siteContextString = NULL;
|
||||||
uint32_t siteCounter = 1;
|
uint32_t siteCounter = 1;
|
||||||
const char *siteCounterString = getenv( MP_env_sitecounter );
|
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.
|
// 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) {
|
switch (opt) {
|
||||||
case 'u':
|
case 'u':
|
||||||
fullName = optarg;
|
fullName = optarg;
|
||||||
break;
|
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':
|
case 't':
|
||||||
siteTypeString = optarg;
|
siteTypeString = optarg;
|
||||||
break;
|
break;
|
||||||
@@ -118,6 +130,10 @@ int main(int argc, char *const argv[]) {
|
|||||||
case 'v':
|
case 'v':
|
||||||
siteVariantString = optarg;
|
siteVariantString = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'V':
|
||||||
|
if (sscanf( optarg, "%u", &algorithmVersion ) != 1)
|
||||||
|
ftl( "Not a version: %s\n", optarg );
|
||||||
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
siteContextString = optarg;
|
siteContextString = optarg;
|
||||||
break;
|
break;
|
||||||
@@ -161,6 +177,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
siteType = MPSiteTypeGeneratedPhrase;
|
siteType = MPSiteTypeGeneratedPhrase;
|
||||||
if (siteTypeString)
|
if (siteTypeString)
|
||||||
siteType = mpw_typeWithName( siteTypeString );
|
siteType = mpw_typeWithName( siteTypeString );
|
||||||
|
trc( "algorithmVersion: %u\n", algorithmVersion );
|
||||||
|
|
||||||
// Read the master password.
|
// Read the master password.
|
||||||
char *mpwConfigPath = homedir( ".mpw" );
|
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 ) );
|
fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, mpw_identicon( fullName, masterPassword ) );
|
||||||
|
|
||||||
// Output the password.
|
// Output the password.
|
||||||
const uint8_t *masterKey = mpw_masterKeyForUser( fullName, masterPassword );
|
const uint8_t *masterKey = mpw_masterKeyForUser(
|
||||||
|
fullName, masterPassword, algorithmVersion );
|
||||||
mpw_freeString( masterPassword );
|
mpw_freeString( masterPassword );
|
||||||
if (!masterKey)
|
if (!masterKey)
|
||||||
ftl( "Couldn't derive master key." );
|
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 );
|
mpw_free( masterKey, MP_dkLen );
|
||||||
if (!sitePassword)
|
if (!sitePassword)
|
||||||
ftl( "Couldn't derive site password." );
|
ftl( "Couldn't derive site password." );
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
|
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
|
||||||
|
|
||||||
#include "mpw-types.h"
|
|
||||||
#include "mpw-algorithm.h"
|
#include "mpw-algorithm.h"
|
||||||
#include "mpw-util.h"
|
#include "mpw-util.h"
|
||||||
|
|
||||||
@@ -40,13 +39,13 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// 1. calculate the master key.
|
// 1. calculate the master key.
|
||||||
const uint8_t *masterKey = mpw_masterKeyForUser(
|
const uint8_t *masterKey = mpw_masterKeyForUser(
|
||||||
(char *)fullName, (char *)masterPassword );
|
(char *)fullName, (char *)masterPassword, MPAlgorithmVersionCurrent );
|
||||||
if (!masterKey)
|
if (!masterKey)
|
||||||
ftl( "Couldn't derive master key." );
|
ftl( "Couldn't derive master key." );
|
||||||
|
|
||||||
// 2. calculate the site password.
|
// 2. calculate the site password.
|
||||||
const char *sitePassword = mpw_passwordForSite(
|
const char *sitePassword = mpw_passwordForSite(
|
||||||
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext );
|
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, MPAlgorithmVersionCurrent );
|
||||||
mpw_free( masterKey, MP_dkLen );
|
mpw_free( masterKey, MP_dkLen );
|
||||||
if (!sitePassword)
|
if (!sitePassword)
|
||||||
ftl( "Couldn't derive site password." );
|
ftl( "Couldn't derive site password." );
|
||||||
|
|||||||
@@ -51,56 +51,71 @@ const MPSiteType mpw_typeWithName(const char *typeName) {
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
|
inline const char **mpw_templatesForType(MPSiteType type, size_t *count) {
|
||||||
|
|
||||||
if (!(type & MPSiteTypeClassGenerated)) {
|
if (!(type & MPSiteTypeClassGenerated)) {
|
||||||
fprintf( stderr, "Not a generated type: %d", type );
|
ftl( "Not a generated type: %d", type );
|
||||||
abort();
|
*count = 0;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case MPSiteTypeGeneratedMaximum: {
|
case MPSiteTypeGeneratedMaximum: {
|
||||||
const char *templates[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
|
*count = 2;
|
||||||
return templates[seedByte % 2];
|
return (const char *[]){ "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedLong: {
|
case MPSiteTypeGeneratedLong: {
|
||||||
const char *templates[] = { "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
*count = 21;
|
||||||
|
return (const char *[]){ "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
||||||
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
||||||
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
||||||
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
||||||
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
||||||
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
||||||
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" };
|
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" };
|
||||||
return templates[seedByte % 21];
|
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedMedium: {
|
case MPSiteTypeGeneratedMedium: {
|
||||||
const char *templates[] = { "CvcnoCvc", "CvcCvcno" };
|
*count = 2;
|
||||||
return templates[seedByte % 2];
|
return (const char *[]){ "CvcnoCvc", "CvcCvcno" };
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedBasic: {
|
case MPSiteTypeGeneratedBasic: {
|
||||||
const char *templates[] = { "aaanaaan", "aannaaan", "aaannaaa" };
|
*count = 3;
|
||||||
return templates[seedByte % 3];
|
return (const char *[]){ "aaanaaan", "aannaaan", "aaannaaa" };
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedShort: {
|
case MPSiteTypeGeneratedShort: {
|
||||||
return "Cvcn";
|
*count = 1;
|
||||||
|
return (const char *[]){"Cvcn"};
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedPIN: {
|
case MPSiteTypeGeneratedPIN: {
|
||||||
return "nnnn";
|
*count = 1;
|
||||||
|
return (const char *[]){ "nnnn" };
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedName: {
|
case MPSiteTypeGeneratedName: {
|
||||||
return "cvccvcvcv";
|
*count = 1;
|
||||||
|
return (const char *[]) {"cvccvcvcv"};
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
case MPSiteTypeGeneratedPhrase: {
|
||||||
const char *templates[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
|
*count = 3;
|
||||||
return templates[seedByte % 3];
|
return (const char *[]){ "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
fprintf( stderr, "Unknown generated type: %d", type );
|
ftl( "Unknown generated type: %d", type );
|
||||||
abort();
|
*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) {
|
const MPSiteVariant mpw_variantWithName(const char *variantName) {
|
||||||
|
|
||||||
char stdVariantName[strlen( 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) {
|
switch (characterClass) {
|
||||||
case 'V': {
|
case 'V':
|
||||||
classCharacters = "AEIOU";
|
return "AEIOU";
|
||||||
break;
|
case 'C':
|
||||||
}
|
return "BCDFGHJKLMNPQRSTVWXYZ";
|
||||||
case 'C': {
|
case 'v':
|
||||||
classCharacters = "BCDFGHJKLMNPQRSTVWXYZ";
|
return "aeiou";
|
||||||
break;
|
case 'c':
|
||||||
}
|
return "bcdfghjklmnpqrstvwxyz";
|
||||||
case 'v': {
|
case 'A':
|
||||||
classCharacters = "aeiou";
|
return "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
|
||||||
break;
|
case 'a':
|
||||||
}
|
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
|
||||||
case 'c': {
|
case 'n':
|
||||||
classCharacters = "bcdfghjklmnpqrstvwxyz";
|
return "0123456789";
|
||||||
break;
|
case 'o':
|
||||||
}
|
return "@&%?,=[]_:-+*$#!'^~;()/.";
|
||||||
case 'A': {
|
case 'x':
|
||||||
classCharacters = "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
|
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
|
||||||
break;
|
case ' ':
|
||||||
}
|
return " ";
|
||||||
case 'a': {
|
|
||||||
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'n': {
|
|
||||||
classCharacters = "0123456789";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'o': {
|
|
||||||
classCharacters = "@&%?,=[]_:-+*$#!'^~;()/.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'x': {
|
|
||||||
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ' ': {
|
|
||||||
classCharacters = " ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
fprintf( stderr, "Unknown character class: %c", characterClass );
|
fprintf( stderr, "Unknown character class: %c", characterClass );
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
|
||||||
|
|
||||||
|
const char *classCharacters = mpw_charactersInClass( characterClass );
|
||||||
return classCharacters[seedByte % strlen( classCharacters )];
|
return classCharacters[seedByte % strlen( classCharacters )];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,32 +6,43 @@
|
|||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
// 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.
|
//// Types.
|
||||||
|
|
||||||
typedef enum {
|
typedef enum( unsigned int, MPSiteVariant ) {
|
||||||
/** Generate the password to log in with. */
|
/** Generate the password to log in with. */
|
||||||
MPSiteVariantPassword,
|
MPSiteVariantPassword,
|
||||||
/** Generate the login name to log in as. */
|
/** Generate the login name to log in as. */
|
||||||
MPSiteVariantLogin,
|
MPSiteVariantLogin,
|
||||||
/** Generate the answer to a security question. */
|
/** Generate the answer to a security question. */
|
||||||
MPSiteVariantAnswer,
|
MPSiteVariantAnswer,
|
||||||
} MPSiteVariant;
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum( unsigned int, MPSiteTypeClass ) {
|
||||||
/** Generate the password. */
|
/** Generate the password. */
|
||||||
MPSiteTypeClassGenerated = 1 << 4,
|
MPSiteTypeClassGenerated = 1 << 4,
|
||||||
/** Store the password. */
|
/** Store the password. */
|
||||||
MPSiteTypeClassStored = 1 << 5,
|
MPSiteTypeClassStored = 1 << 5,
|
||||||
} MPSiteTypeClass;
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum( unsigned int, MPSiteFeature ) {
|
||||||
/** Export the key-protected content data. */
|
/** Export the key-protected content data. */
|
||||||
MPSiteFeatureExportContent = 1 << 10,
|
MPSiteFeatureExportContent = 1 << 10,
|
||||||
/** Never export content. */
|
/** Never export content. */
|
||||||
MPSiteFeatureDevicePrivate = 1 << 11,
|
MPSiteFeatureDevicePrivate = 1 << 11,
|
||||||
} MPSiteFeature;
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum( unsigned int, MPSiteType) {
|
||||||
MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
|
||||||
MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
|
||||||
MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
|
MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
|
||||||
@@ -43,13 +54,42 @@ typedef enum {
|
|||||||
|
|
||||||
MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
|
MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
|
||||||
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
|
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
|
||||||
} MPSiteType;
|
};
|
||||||
|
|
||||||
//// Type utilities.
|
//// Type utilities.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The variant represented by the given name.
|
||||||
|
*/
|
||||||
const MPSiteVariant mpw_variantWithName(const char *variantName);
|
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);
|
const char *mpw_scopeForVariant(MPSiteVariant variant);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The type represented by the given name.
|
||||||
|
*/
|
||||||
const MPSiteType mpw_typeWithName(const char *typeName);
|
const MPSiteType mpw_typeWithName(const char *typeName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An array of internal strings that express the templates to use for the given type.
|
||||||
|
* 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);
|
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);
|
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
#include <scrypt/sha256.h>
|
#include <scrypt/sha256.h>
|
||||||
#include <scrypt/crypto_scrypt.h>
|
#include <scrypt/crypto_scrypt.h>
|
||||||
@@ -163,3 +164,9 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
|||||||
free( resetString );
|
free( resetString );
|
||||||
return identicon;
|
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.
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
//// Logging.
|
//// Logging.
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
#ifndef trc
|
||||||
#define trc(...) fprintf( stderr, __VA_ARGS__ )
|
#define trc(...) fprintf( stderr, __VA_ARGS__ )
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
|
#ifndef trc
|
||||||
#define trc(...) do {} while (0)
|
#define trc(...) do {} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#ifndef ftl
|
#ifndef ftl
|
||||||
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); abort(); } while (0)
|
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); abort(); } while (0)
|
||||||
#endif
|
#endif
|
||||||
@@ -50,11 +56,15 @@ uint8_t const *mpw_hmac_sha256(
|
|||||||
//// Visualizers.
|
//// Visualizers.
|
||||||
|
|
||||||
/** Encode a buffer as a string of hexadecimal characters.
|
/** 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);
|
const char *mpw_hex(const void *buf, size_t length);
|
||||||
/** Encode a fingerprint for a buffer.
|
/** 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);
|
const char *mpw_idForBuf(const void *buf, size_t length);
|
||||||
/** Encode a visual fingerprint for a user.
|
/** Encode a visual fingerprint for a user.
|
||||||
* @return A newly allocated string. */
|
* @return A newly allocated string. */
|
||||||
const char *mpw_identicon(const char *fullName, const char *masterPassword);
|
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>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password Algorithm Implementation</name>
|
<name>Master Password Algorithm Implementation</name>
|
||||||
<description>The implementation of the Master Password algorithm</description>
|
<description>The implementation of the Master Password algorithm</description>
|
||||||
|
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
<artifactId>masterpassword-algorithm</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
@@ -24,12 +23,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||||
<artifactId>opal-system</artifactId>
|
<artifactId>opal-system</artifactId>
|
||||||
<version>1.6-p6</version>
|
<version>1.6-p8</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.lhunath.opal</groupId>
|
<groupId>com.lyndir.lhunath.opal</groupId>
|
||||||
<artifactId>opal-crypto</artifactId>
|
<artifactId>opal-crypto</artifactId>
|
||||||
<version>1.6-p6</version>
|
<version>1.6-p8</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- EXTERNAL DEPENDENCIES -->
|
<!-- 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.
|
* @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)
|
if (name == null)
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.lyndir.masterpassword;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,11 +62,11 @@ public enum MPSiteVariant {
|
|||||||
throw logger.bug( "No variant for option: %s", option );
|
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.
|
* @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)
|
if (name == null)
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ package com.lyndir.masterpassword;
|
|||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
|
||||||
import com.lyndir.lhunath.opal.system.util.ObjectMeta;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,154 +1,164 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.primitives.Bytes;
|
|
||||||
import com.lambdaworks.crypto.SCrypt;
|
|
||||||
import com.lyndir.lhunath.opal.system.*;
|
import com.lyndir.lhunath.opal.system.*;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lhunath, 2014-08-30
|
* @author lhunath, 2014-08-30
|
||||||
*/
|
*/
|
||||||
public class MasterKey {
|
public abstract class MasterKey {
|
||||||
|
|
||||||
public static final int ALGORITHM = 1;
|
|
||||||
public static final String VERSION = "2.1";
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
private static final Logger logger = Logger.get( MasterKey.class );
|
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;
|
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
private final String fullName;
|
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;
|
this.fullName = fullName;
|
||||||
logger.trc( "fullName: %s", 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() {
|
public String getFullName() {
|
||||||
|
|
||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected byte[] getKey() {
|
||||||
|
|
||||||
|
return Preconditions.checkNotNull( masterKey );
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getKeyID() {
|
public byte[] getKeyID() {
|
||||||
|
|
||||||
Preconditions.checkState( valid );
|
return idForBytes( getKey() );
|
||||||
return idForBytes( masterKey );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 );
|
public boolean isValid() {
|
||||||
byte[] subkey = new byte[Math.min( subkeyLength, masterKey.length )];
|
return masterKey != null;
|
||||||
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 void invalidate() {
|
public void invalidate() {
|
||||||
|
|
||||||
valid = false;
|
if (masterKey != null) {
|
||||||
Arrays.fill( masterKey, (byte) 0 );
|
Arrays.fill( masterKey, (byte) 0 );
|
||||||
|
masterKey = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] bytesForInt(final int integer) {
|
public MasterKey revalidate(final char[] masterPassword) {
|
||||||
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
|
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) {
|
protected abstract byte[] bytesForInt(final int integer);
|
||||||
return MP_hash.of( bytes );
|
|
||||||
|
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
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public String get() {
|
public String get() {
|
||||||
return parentCase.getMasterPassword();
|
return new String( parentCase.getMasterPassword() );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
|
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
|
||||||
@@ -148,8 +148,8 @@ public class MPWTests {
|
|||||||
return fullName;
|
return fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMasterPassword() {
|
public char[] getMasterPassword() {
|
||||||
return masterPassword;
|
return masterPassword == null? null: masterPassword.toCharArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKeyID() {
|
public String getKeyID() {
|
||||||
@@ -161,7 +161,7 @@ public class MPWTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getSiteCounter() {
|
public int getSiteCounter() {
|
||||||
return siteCounter;
|
return ifNotNullElse( siteCounter, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPSiteType getSiteType() {
|
public MPSiteType getSiteType() {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
for (MPWTests.Case testCase : tests.getCases()) {
|
for (MPWTests.Case testCase : tests.getCases()) {
|
||||||
MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
|
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
||||||
assertEquals(
|
assertEquals(
|
||||||
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
||||||
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
||||||
@@ -46,7 +46,7 @@ public class MasterKeyTest {
|
|||||||
public void testGetUserName()
|
public void testGetUserName()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
assertEquals( new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
|
assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
|
||||||
defaultCase.getFullName() );
|
defaultCase.getFullName() );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
for (MPWTests.Case testCase : tests.getCases()) {
|
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 );
|
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MasterKey masterKey = new MasterKey( defaultCase.getFullName(), defaultCase.getMasterPassword() );
|
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
|
||||||
masterKey.invalidate();
|
masterKey.invalidate();
|
||||||
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
|
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
|
||||||
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
|
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.lyndir.masterpassword"
|
package="com.lyndir.masterpassword"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:versionName="GIT-SNAPSHOT">
|
android:versionName="2.2">
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="14"
|
android:minSdkVersion="19"
|
||||||
android:targetSdkVersion="19" />
|
android:targetSdkVersion="21" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@drawable/icon"
|
android:icon="@drawable/icon"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:allowBackup="true">
|
android:allowBackup="true">
|
||||||
<activity android:name=".EmergencyActivity">
|
<activity android:name=".EmergencyActivity" android:theme="@style/MPTheme">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<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>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password Android</name>
|
<name>Master Password Android</name>
|
||||||
<description>An Android application to the Master Password algorithm</description>
|
<description>An Android application to the Master Password algorithm</description>
|
||||||
|
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
|
||||||
<artifactId>masterpassword-android</artifactId>
|
<artifactId>masterpassword-android</artifactId>
|
||||||
<packaging>apk</packaging>
|
<packaging>apk</packaging>
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
<skip>false</skip>
|
<skip>false</skip>
|
||||||
</zipalign>
|
</zipalign>
|
||||||
<sdk>
|
<sdk>
|
||||||
<platform>19</platform>
|
<platform>21</platform>
|
||||||
</sdk>
|
</sdk>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
@@ -39,9 +38,32 @@
|
|||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>sign</id>
|
<id>release</id>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||||
|
<artifactId>android-maven-plugin</artifactId>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
<sign>
|
||||||
|
<debug>false</debug>
|
||||||
|
</sign>
|
||||||
|
</configuration>
|
||||||
|
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>manifest-update</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>manifest-update</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<manifestVersionCodeUpdateFromVersion>true</manifestVersionCodeUpdateFromVersion>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jarsigner-plugin</artifactId>
|
<artifactId>maven-jarsigner-plugin</artifactId>
|
||||||
@@ -54,14 +76,14 @@
|
|||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
<inherited>true</inherited>
|
<inherited>true</inherited>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archiveDirectory></archiveDirectory>
|
<archiveDirectory />
|
||||||
<includes>
|
<includes>
|
||||||
<include>target/*.apk</include>
|
<include>target/*.apk</include>
|
||||||
</includes>
|
</includes>
|
||||||
<keystore>release.jks</keystore>
|
<keystore>release.jks</keystore>
|
||||||
<storepass>${env.PASSWORD}</storepass>
|
<storepass>${env.PASSWORD}</storepass>
|
||||||
<keypass>${env.PASSWORD}</keypass>
|
<keypass>${env.PASSWORD}</keypass>
|
||||||
<alias>android</alias>
|
<alias>masterpassword-android</alias>
|
||||||
<arguments>
|
<arguments>
|
||||||
<argument>-sigalg</argument><argument>MD5withRSA</argument>
|
<argument>-sigalg</argument><argument>MD5withRSA</argument>
|
||||||
<argument>-digestalg</argument><argument>SHA1</argument>
|
<argument>-digestalg</argument><argument>SHA1</argument>
|
||||||
@@ -70,16 +92,6 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
|
||||||
<artifactId>android-maven-plugin</artifactId>
|
|
||||||
<inherited>true</inherited>
|
|
||||||
<configuration>
|
|
||||||
<sign>
|
|
||||||
<debug>false</debug>
|
|
||||||
</sign>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
@@ -92,7 +104,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
<artifactId>masterpassword-algorithm</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -114,11 +126,10 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lambdaworks</groupId>
|
<groupId>com.lambdaworks</groupId>
|
||||||
<artifactId>libscrypt</artifactId>
|
<artifactId>scrypt</artifactId>
|
||||||
<version>1.4.0</version>
|
<version>1.4.0-android</version>
|
||||||
<type>so</type>
|
<type>jar</type>
|
||||||
<classifier>android</classifier>
|
<classifier>native</classifier>
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</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
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="20dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:gravity="center">
|
android:gravity="center">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="0dp"
|
android:layout_width="1dp"
|
||||||
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_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/userNameField"
|
android:id="@+id/fullNameField"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:nextFocusForward="@+id/masterPasswordField"
|
||||||
android:inputType="text|textCapWords|textPersonName"
|
android:inputType="text|textCapWords|textPersonName"
|
||||||
android:hint="@string/userName.hint"
|
android:hint="@string/fullName_hint"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="#FFFFFF"
|
android:textColor="#FFFFFF"
|
||||||
android:textSize="26sp" />
|
android:textSize="26sp" />
|
||||||
|
|
||||||
<EditText
|
<CheckBox
|
||||||
android:id="@+id/masterPasswordField"
|
android:id="@+id/rememberFullNameField"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
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:inputType="text|textPassword"
|
||||||
android:hint="@string/masterPassword.hint"
|
android:hint="@string/masterPassword_hint"
|
||||||
android:password="true"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="#FFFFFF"
|
android:textColor="#FFFFFF"
|
||||||
android:textSize="18sp" />
|
android:textSize="18sp" />
|
||||||
|
|
||||||
<ImageView
|
<CheckBox
|
||||||
android:layout_width="wrap_content"
|
android:id="@id/rememberPasswordField"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:src="@drawable/double_"
|
android:textSize="14sp"
|
||||||
android:contentDescription="@string/empty" />
|
android:textColor="@android:color/tertiary_text_dark"
|
||||||
|
android:text="@string/forgetOnClose" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/siteNameField"
|
android:id="@id/siteNameField"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:nextFocusForward="@+id/sitePasswordField"
|
||||||
android:inputType="text|textNoSuggestions|textUri"
|
android:inputType="text|textNoSuggestions|textUri"
|
||||||
android:hint="@string/siteName.hint"
|
android:hint="@string/siteName_hint"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="#FFFFFF"
|
android:textColor="#FFFFFF"
|
||||||
android:textSize="26sp" />
|
android:textSize="18sp" />
|
||||||
|
|
||||||
<Button
|
<FrameLayout
|
||||||
android:id="@+id/sitePasswordField"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
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:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:textSize="14sp"
|
||||||
android:background="@null"
|
android:textColor="@android:color/tertiary_text_dark"
|
||||||
android:textColor="#FFFFFF"
|
android:text="@string/maskPassword" />
|
||||||
android:textSize="32sp"
|
|
||||||
android:text="LuxdZozvDuma4["
|
|
||||||
android:onClick="copySitePassword" />
|
|
||||||
|
|
||||||
<Spinner
|
<Spinner
|
||||||
android:id="@+id/typeField"
|
android:id="@id/siteTypeField"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:nextFocusForward="@+id/counterField"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
<NumberPicker
|
<EditText
|
||||||
android:id="@+id/counterField"
|
android:id="@id/counterField"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
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>
|
</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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Master Password</string>
|
<string name="app_name">Master Password</string>
|
||||||
<string name="avatar">User Avatar</string>
|
<string name="remember">Remember</string>
|
||||||
<string name="siteName.hint">Site Name</string>
|
<string name="forgetOnClose">Forget on close</string>
|
||||||
<string name="userName.hint">Your Name</string>
|
<string name="maskPassword">Hide password</string>
|
||||||
<string name="masterPassword.hint">Your Master 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" />
|
<string name="empty" />
|
||||||
</resources>
|
</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.app.Activity;
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.*;
|
||||||
import android.text.TextWatcher;
|
import android.text.method.PasswordTransformationMethod;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.InjectView;
|
import butterknife.InjectView;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.util.concurrent.*;
|
import com.google.common.util.concurrent.*;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
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 java.util.concurrent.*;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
public class EmergencyActivity extends Activity {
|
public class EmergencyActivity extends Activity {
|
||||||
@@ -42,8 +47,8 @@ public class EmergencyActivity extends Activity {
|
|||||||
@InjectView(R.id.progressView)
|
@InjectView(R.id.progressView)
|
||||||
ProgressBar progressView;
|
ProgressBar progressView;
|
||||||
|
|
||||||
@InjectView(R.id.userNameField)
|
@InjectView(R.id.fullNameField)
|
||||||
EditText userNameField;
|
EditText fullNameField;
|
||||||
|
|
||||||
@InjectView(R.id.masterPasswordField)
|
@InjectView(R.id.masterPasswordField)
|
||||||
EditText masterPasswordField;
|
EditText masterPasswordField;
|
||||||
@@ -51,101 +56,184 @@ public class EmergencyActivity extends Activity {
|
|||||||
@InjectView(R.id.siteNameField)
|
@InjectView(R.id.siteNameField)
|
||||||
EditText siteNameField;
|
EditText siteNameField;
|
||||||
|
|
||||||
@InjectView(R.id.typeField)
|
@InjectView(R.id.siteTypeField)
|
||||||
Spinner typeField;
|
Spinner siteTypeField;
|
||||||
|
|
||||||
@InjectView(R.id.counterField)
|
@InjectView(R.id.counterField)
|
||||||
NumberPicker counterField;
|
EditText counterField;
|
||||||
|
|
||||||
|
@InjectView(R.id.siteVersionField)
|
||||||
|
Spinner siteVersionField;
|
||||||
|
|
||||||
@InjectView(R.id.sitePasswordField)
|
@InjectView(R.id.sitePasswordField)
|
||||||
TextView sitePasswordField;
|
TextView sitePasswordField;
|
||||||
|
|
||||||
private int hc_userName;
|
@InjectView(R.id.sitePasswordTip)
|
||||||
private int hc_masterPassword;
|
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
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate( savedInstanceState );
|
super.onCreate( savedInstanceState );
|
||||||
Res.init( getResources() );
|
Res.init( getResources() );
|
||||||
|
|
||||||
|
getWindow().setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE );
|
||||||
setContentView( R.layout.activity_emergency );
|
setContentView( R.layout.activity_emergency );
|
||||||
ButterKnife.inject( this );
|
ButterKnife.inject( this );
|
||||||
|
|
||||||
userNameField.setOnFocusChangeListener( updateMasterKey );
|
fullNameField.setOnFocusChangeListener( updateMasterKey );
|
||||||
masterPasswordField.setOnFocusChangeListener( updateMasterKey );
|
masterPasswordField.setOnFocusChangeListener( updateMasterKey );
|
||||||
siteNameField.addTextChangedListener( updateSitePassword );
|
siteNameField.addTextChangedListener( updateSitePassword );
|
||||||
typeField.setOnItemSelectedListener( updateSitePassword );
|
siteTypeField.setOnItemSelectedListener( updateSitePassword );
|
||||||
counterField.setOnValueChangedListener( 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 );
|
if (noPassword)
|
||||||
userNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
sitePassword = null;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
fullNameField.setTypeface( Res.exo_Thin );
|
||||||
|
fullNameField.setPaintFlags( fullNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
|
||||||
masterPasswordField.setTypeface( Res.sourceCodePro_ExtraLight );
|
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.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.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 ) ) );
|
siteTypeField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) );
|
||||||
typeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
|
siteTypeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
|
||||||
|
|
||||||
counterField.setMinValue( 1 );
|
siteVersionField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MasterKey.Version.values() ) );
|
||||||
counterField.setMaxValue( Integer.MAX_VALUE );
|
siteVersionField.setSelection( MasterKey.Version.CURRENT.ordinal() );
|
||||||
counterField.setWrapSelectorWheel( false );
|
|
||||||
|
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
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
userNameField.setText( getPreferences( MODE_PRIVATE ).getString( "userName", "" ) );
|
fullNameField.setText( getPreferences( MODE_PRIVATE ).getString( "fullName", "" ) );
|
||||||
masterPasswordField.requestFocus();
|
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
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
synchronized (this) {
|
if (isForgetPasswordEnabled()) {
|
||||||
hc_userName = hc_masterPassword = 0;
|
synchronized (this) {
|
||||||
if (masterKeyFuture != null) {
|
hc_userName = hc_masterPassword = 0;
|
||||||
masterKeyFuture.cancel( true );
|
if (masterKeyFuture != null) {
|
||||||
masterKeyFuture = null;
|
masterKeyFuture.cancel( true );
|
||||||
|
masterKeyFuture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
masterPasswordField.setText( "" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
siteNameField.setText( "" );
|
||||||
sitePasswordField.setText( "" );
|
sitePasswordField.setText( "" );
|
||||||
progressView.setVisibility( View.INVISIBLE );
|
progressView.setVisibility( View.INVISIBLE );
|
||||||
|
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void updateMasterKey() {
|
private boolean isRememberFullNameEnabled() {
|
||||||
final String userName = userNameField.getText().toString();
|
return getPreferences( MODE_PRIVATE ).getBoolean( "rememberFullName", false );
|
||||||
final String masterPassword = masterPasswordField.getText().toString();
|
}
|
||||||
if (userName.hashCode() == hc_userName && masterPassword.hashCode() == hc_masterPassword)
|
|
||||||
return;
|
|
||||||
hc_userName = userName.hashCode();
|
|
||||||
hc_masterPassword = masterPassword.hashCode();
|
|
||||||
|
|
||||||
SharedPreferences.Editor pref = getPreferences( MODE_PRIVATE ).edit();
|
private boolean isForgetPasswordEnabled() {
|
||||||
pref.putString( "userName", userName );
|
return getPreferences( MODE_PRIVATE ).getBoolean( "forgetPassword", false );
|
||||||
pref.apply();
|
}
|
||||||
|
|
||||||
|
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)
|
if (masterKeyFuture != null)
|
||||||
masterKeyFuture.cancel( true );
|
masterKeyFuture.cancel( true );
|
||||||
|
|
||||||
if (userName.isEmpty() || masterPassword.isEmpty()) {
|
if (fullName.isEmpty() || masterPassword.length == 0) {
|
||||||
sitePasswordField.setText( "" );
|
sitePasswordField.setText( "" );
|
||||||
progressView.setVisibility( View.INVISIBLE );
|
progressView.setVisibility( View.INVISIBLE );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sitePasswordField.setText( "" );
|
||||||
progressView.setVisibility( View.VISIBLE );
|
progressView.setVisibility( View.VISIBLE );
|
||||||
(masterKeyFuture = executor.submit( new Callable<MasterKey>() {
|
(masterKeyFuture = executor.submit( new Callable<MasterKey>() {
|
||||||
@Override
|
@Override
|
||||||
public MasterKey call()
|
public MasterKey call()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
try {
|
try {
|
||||||
return new MasterKey( userName, masterPassword );
|
return MasterKey.create( version, fullName, masterPassword );
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
catch (RuntimeException e) {
|
||||||
sitePasswordField.setText( "" );
|
sitePasswordField.setText( "" );
|
||||||
@@ -169,21 +257,25 @@ public class EmergencyActivity extends Activity {
|
|||||||
|
|
||||||
private void updateSitePassword() {
|
private void updateSitePassword() {
|
||||||
final String siteName = siteNameField.getText().toString();
|
final String siteName = siteNameField.getText().toString();
|
||||||
final MPSiteType type = (MPSiteType) typeField.getSelectedItem();
|
final MPSiteType type = (MPSiteType) siteTypeField.getSelectedItem();
|
||||||
final int counter = counterField.getValue();
|
final int counter = ConversionUtils.toIntegerNN( counterField.getText() );
|
||||||
|
|
||||||
if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
|
if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
|
||||||
sitePasswordField.setText( "" );
|
sitePasswordField.setText( "" );
|
||||||
progressView.setVisibility( View.INVISIBLE );
|
progressView.setVisibility( View.INVISIBLE );
|
||||||
|
|
||||||
|
if (masterKeyFuture == null)
|
||||||
|
updateMasterKey();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sitePasswordField.setText( "" );
|
||||||
progressView.setVisibility( View.VISIBLE );
|
progressView.setVisibility( View.VISIBLE );
|
||||||
executor.submit( new Runnable() {
|
executor.submit( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
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() {
|
runOnUiThread( new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -214,19 +306,18 @@ public class EmergencyActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void copySitePassword(View view) {
|
public void copySitePassword(View view) {
|
||||||
String sitePassword = sitePasswordField.getText().toString();
|
if (TextUtils.isEmpty( sitePassword ))
|
||||||
if (sitePassword.isEmpty())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ClipDescription description = new ClipDescription( strf( "Password for %s", siteNameField.getText() ),
|
ClipDescription description = new ClipDescription( strf( "Password for %s", siteNameField.getText() ),
|
||||||
new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } );
|
new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } );
|
||||||
((ClipboardManager) getSystemService( CLIPBOARD_SERVICE )).setPrimaryClip(
|
ClipData clipData = new ClipData( description, new ClipData.Item( sitePassword ) );
|
||||||
new ClipData( description, new ClipData.Item( sitePassword ) ) );
|
((ClipboardManager) getSystemService( CLIPBOARD_SERVICE )).setPrimaryClip( clipData );
|
||||||
|
|
||||||
Intent startMain = new Intent(Intent.ACTION_MAIN);
|
Intent startMain = new Intent( Intent.ACTION_MAIN );
|
||||||
startMain.addCategory(Intent.CATEGORY_HOME);
|
startMain.addCategory( Intent.CATEGORY_HOME );
|
||||||
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
startMain.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
|
||||||
startActivity(startMain);
|
startActivity( startMain );
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class ValueChangedListener
|
private abstract class ValueChangedListener
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password CLI</name>
|
<name>Master Password CLI</name>
|
||||||
<description>A CLI interface to the Master Password algorithm</description>
|
<description>A CLI interface to the Master Password algorithm</description>
|
||||||
|
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
|
||||||
<artifactId>masterpassword-cli</artifactId>
|
<artifactId>masterpassword-cli</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@
|
|||||||
<phase>prepare-package</phase>
|
<phase>prepare-package</phase>
|
||||||
<configuration>
|
<configuration>
|
||||||
<target>
|
<target>
|
||||||
<chmod file="${project.build.directory}/install" perm="755"/>
|
<chmod file="${project.build.directory}/install" perm="755" />
|
||||||
</target>
|
</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
<goals>
|
<goals>
|
||||||
@@ -86,7 +85,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
<artifactId>masterpassword-algorithm</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ public class CLI {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
// Read information from the environment.
|
// 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 userName = System.getenv( ENV_USERNAME );
|
||||||
String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" );
|
String siteTypeName = ifNotNullElse( System.getenv( ENV_SITETYPE ), "" );
|
||||||
MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
|
MPSiteType siteType = siteTypeName.isEmpty()? MPSiteType.GeneratedLong: MPSiteType.forOption( siteTypeName );
|
||||||
@@ -174,15 +175,15 @@ public class CLI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (console != null)
|
if (console != null)
|
||||||
masterPassword = new String( console.readPassword( "%s's master password: ", userName ) );
|
masterPassword = console.readPassword( "%s's master password: ", userName );
|
||||||
|
|
||||||
else {
|
else {
|
||||||
System.err.format( "%s's master password: ", userName );
|
System.err.format( "%s's master password: ", userName );
|
||||||
masterPassword = lineReader.readLine();
|
masterPassword = lineReader.readLine().toCharArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode and write out the site password.
|
// 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>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password GUI</name>
|
<name>Master Password GUI</name>
|
||||||
<description>A GUI interface to the Master Password algorithm</description>
|
<description>A GUI interface to the Master Password algorithm</description>
|
||||||
|
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
|
||||||
<artifactId>masterpassword-gui</artifactId>
|
<artifactId>masterpassword-gui</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
@@ -67,6 +66,67 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>release</id>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||||
|
<artifactId>android-maven-plugin</artifactId>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
<sign>
|
||||||
|
<debug>false</debug>
|
||||||
|
</sign>
|
||||||
|
</configuration>
|
||||||
|
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>manifest-update</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>manifest-update</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<manifestVersionCodeUpdateFromVersion>true</manifestVersionCodeUpdateFromVersion>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jarsigner-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>signing</id>
|
||||||
|
<goals>
|
||||||
|
<goal>sign</goal>
|
||||||
|
</goals>
|
||||||
|
<phase>package</phase>
|
||||||
|
<inherited>true</inherited>
|
||||||
|
<configuration>
|
||||||
|
<archiveDirectory />
|
||||||
|
<includes>
|
||||||
|
<include>target/*.jar</include>
|
||||||
|
</includes>
|
||||||
|
<keystore>release.jks</keystore>
|
||||||
|
<storepass>${env.PASSWORD}</storepass>
|
||||||
|
<keypass>${env.PASSWORD}</keypass>
|
||||||
|
<alias>masterpassword-desktop</alias>
|
||||||
|
<arguments>
|
||||||
|
<argument>-sigalg</argument><argument>MD5withRSA</argument>
|
||||||
|
<argument>-digestalg</argument><argument>SHA1</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
<!-- DEPENDENCY MANAGEMENT -->
|
<!-- DEPENDENCY MANAGEMENT -->
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
@@ -74,7 +134,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword-model</artifactId>
|
<artifactId>masterpassword-model</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- EXTERNAL DEPENDENCIES -->
|
<!-- EXTERNAL DEPENDENCIES -->
|
||||||
|
|||||||