Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f5bb9e114 | ||
|
|
4c4aaac08b | ||
|
|
5320e079ce | ||
|
|
636c337c78 | ||
|
|
c2b08678df | ||
|
|
7117d82aa4 | ||
|
|
4fa6436de2 | ||
|
|
3e30087856 | ||
|
|
6bd2d70270 | ||
|
|
cf11b01ed6 | ||
|
|
7e11f01331 | ||
|
|
aeca5e18fe | ||
|
|
615e7c98a8 | ||
|
|
e713776123 | ||
|
|
7d5b7e53d4 | ||
|
|
950c68437a | ||
|
|
300a04f5c7 | ||
|
|
8faf6b48dd | ||
|
|
5f0367ad29 | ||
|
|
e126a55912 | ||
|
|
d05c5eedd8 | ||
|
|
6819a2ace5 | ||
|
|
645b6c5f54 | ||
|
|
d3c09fd979 | ||
|
|
a41ae1814a | ||
|
|
70f7fa1345 | ||
|
|
ea9d8cc275 | ||
|
|
20d1811b5c | ||
|
|
634ef062f3 | ||
|
|
a424531a8a | ||
|
|
9cffe53993 | ||
|
|
fd35fea8cf | ||
|
|
01c21e95bb | ||
|
|
5af3ffa178 | ||
|
|
651d07f982 | ||
|
|
a383d0eee7 | ||
|
|
ca8f14fd3e | ||
|
|
fd855bb025 | ||
|
|
af340806af | ||
|
|
cdeee2576d | ||
|
|
779d2776a0 | ||
|
|
563aab9a81 | ||
|
|
73de98c5e2 | ||
|
|
c330728ac3 | ||
|
|
2db601475f | ||
|
|
d898646097 | ||
|
|
afaa17948f | ||
|
|
8514a58e64 | ||
|
|
f80bbff46e | ||
|
|
f7d595b0e7 | ||
|
|
422066ad4a | ||
|
|
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 |
4
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
# OS-Specific junk.
|
# OS-Specific junk.
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
/MasterPassword/Java/.idea
|
/MasterPassword/Java/.idea
|
||||||
@@ -28,9 +29,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
|
||||||
|
|||||||
8
.idea/encodings.xml
generated
Normal file → Executable file
@@ -1,5 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
|
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-algorithm" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-cli" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-gui" charset="UTF-8" />
|
||||||
|
<file url="file://$PROJECT_DIR$/MasterPassword/Java/masterpassword-model" charset="UTF-8" />
|
||||||
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
|
|||||||
2
External/Pearl
vendored
@@ -218,3 +218,8 @@ OBJC_EXTERN void CLSNSLogv(NSString *format, va_list args) NS_FORMAT_FUNCTION(1,
|
|||||||
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
|
- (void)crashlytics:(Crashlytics *)crashlytics didDetectCrashDuringPreviousExecution:(id <CLSCrashReport>)crash;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `CrashlyticsKit` can be used as a parameter to `[Fabric with:@[CrashlyticsKit]];` in Objective-C. In Swift, simply use `Crashlytics()`
|
||||||
|
*/
|
||||||
|
#define CrashlyticsKit [Crashlytics sharedInstance]
|
||||||
|
|||||||
@@ -15,13 +15,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2.2.5</string>
|
<string>2.2.9</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>40</string>
|
<string>44</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>iphoneos</string>
|
<string>iphoneos</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
|
|||||||
BIN
External/iOS/Crashlytics.framework/run
vendored
BIN
External/iOS/Crashlytics.framework/submit
vendored
61
MasterPassword/C/bashcomplib
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# FIXME
|
||||||
|
# partials are currently readline words, but these can't be reliably compared against literal data. We need to make them literal first.. in a safe way. Currently using xargs' quote parser as a hack.
|
||||||
|
|
||||||
|
# Process literal completion options in COMPREPLY
|
||||||
|
#
|
||||||
|
# 1. Filter COMPREPLY by excluding the options that do not match the word that is being completed.
|
||||||
|
# 2. Shell-escape the COMPREPLY words so they remain syntactical words when injected into the completed command.
|
||||||
|
# 3. Add a space after the words so successful completions advance to the next word
|
||||||
|
# (we disabled this default behavior with -o nospace so we can do completions that don't want this, eg. directory names)
|
||||||
|
_comp_finish_completions() {
|
||||||
|
local partial=$(xargs <<< "${COMP_WORDS[COMP_CWORD]}") # FIXME
|
||||||
|
local word words=( "${COMPREPLY[@]}" )
|
||||||
|
|
||||||
|
COMPREPLY=()
|
||||||
|
for word in "${words[@]}"; do
|
||||||
|
( shopt -s nocasematch; [[ $word = $partial* ]] ) && COMPREPLY+=( "$(printf '%q ' "$word")" )
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ${#COMPREPLY[@]} > 1 )) && [[ $_comp_title ]]; then
|
||||||
|
printf '\n%s:' "$_comp_title"
|
||||||
|
unset _comp_title
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Perform pathname completion.
|
||||||
|
#
|
||||||
|
# 1. Populate COMPREPLY with pathnames.
|
||||||
|
# 2. Shell-escape the COMPREPLY words so they remain syntactical words when injected into the completed command.
|
||||||
|
# 3. Add a space after file names so successful completions advance to the next word.
|
||||||
|
# Directory names are suffixed with a / instead so we can keep completing the files inside.
|
||||||
|
_comp_complete_path() {
|
||||||
|
local partial=$(xargs <<< "${COMP_WORDS[COMP_CWORD]}")
|
||||||
|
local path
|
||||||
|
|
||||||
|
COMPREPLY=()
|
||||||
|
for path in "$partial"*; do
|
||||||
|
if [[ -d $path ]]; then
|
||||||
|
COMPREPLY+=( "$(printf '%q/' "$path")" )
|
||||||
|
|
||||||
|
elif [[ -e $path ]]; then
|
||||||
|
COMPREPLY+=( "$(printf '%q ' "$path")" )
|
||||||
|
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_show_args() {
|
||||||
|
echo
|
||||||
|
local i=0
|
||||||
|
for arg; do
|
||||||
|
printf "arg %d: %s\n" "$((i++))" "$arg"
|
||||||
|
done
|
||||||
|
|
||||||
|
i=0
|
||||||
|
for word in "${COMP_WORDS[@]}"; do
|
||||||
|
printf "word %d: %s -> %s %s\n" "$i" "$word" "$(xargs <<< "$word")" "$( ((i == $COMP_CWORD)) && echo '<CWORD>' )"
|
||||||
|
let i++
|
||||||
|
done
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Optional features.
|
# Optional features.
|
||||||
mpw_color=0 # Colorized Identicon, requires libncurses-dev
|
mpw_color=1 # Colorized Identicon, requires libncurses-dev
|
||||||
|
|
||||||
|
|
||||||
### DEPENDENCIES
|
### DEPENDENCIES
|
||||||
@@ -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() {
|
||||||
|
|
||||||
@@ -197,11 +204,11 @@ mpw() {
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Building target: $target..."
|
echo "Building target: $target..."
|
||||||
CFLAGS=(
|
local CFLAGS=(
|
||||||
# include paths
|
# include paths
|
||||||
-I"lib/include"
|
-I"lib/include"
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
local LDFLAGS=(
|
||||||
# scrypt
|
# scrypt
|
||||||
"lib/scrypt/scrypt-crypto_aesctr.o"
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
"lib/scrypt/scrypt-sha256.o"
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
@@ -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"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,11 +240,11 @@ mpw-bench() {
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Building target: $target..."
|
echo "Building target: $target..."
|
||||||
CFLAGS=(
|
local CFLAGS=(
|
||||||
# include paths
|
# include paths
|
||||||
-I"lib/include"
|
-I"lib/include"
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
local LDFLAGS=(
|
||||||
# scrypt
|
# scrypt
|
||||||
"lib/scrypt/scrypt-crypto_aesctr.o"
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
"lib/scrypt/scrypt-sha256.o"
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
@@ -257,8 +264,11 @@ mpw-bench() {
|
|||||||
-l"crypto"
|
-l"crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
||||||
mpw-bench.c -o mpw-bench
|
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
||||||
|
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
|
||||||
echo "done! Now use ./mpw-bench"
|
echo "done! Now use ./mpw-bench"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,12 +279,13 @@ mpw-tests() {
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Building target: $target..."
|
echo "Building target: $target..."
|
||||||
CFLAGS=(
|
local CFLAGS=(
|
||||||
# include paths
|
# include paths
|
||||||
-I"lib/include"
|
-I"lib/include"
|
||||||
-I"/usr/include/libxml2"
|
-I"/usr/include/libxml2"
|
||||||
|
-I"/usr/local/include/libxml2"
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
local LDFLAGS=(
|
||||||
# scrypt
|
# scrypt
|
||||||
"lib/scrypt/scrypt-crypto_aesctr.o"
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
"lib/scrypt/scrypt-sha256.o"
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
@@ -288,9 +299,12 @@ mpw-tests() {
|
|||||||
-l"crypto" -l"xml2"
|
-l"crypto" -l"xml2"
|
||||||
)
|
)
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
|
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
||||||
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
|
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
|
||||||
mpw-tests.c -o mpw-tests
|
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" -c mpw-tests-util.c -o mpw-tests-util.o
|
||||||
|
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" "mpw-tests-util.o" \
|
||||||
|
"${LDFLAGS[@]}" "mpw-tests.c" -o "mpw-tests"
|
||||||
echo "done! Now use ./mpw-tests"
|
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,28 @@
|
|||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#define MP_dkLen 64
|
// NOTE: mpw is currently NOT thread-safe.
|
||||||
|
#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);
|
||||||
|
|||||||
132
MasterPassword/C/mpw-algorithm_v0.c
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
//
|
||||||
|
// 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-types.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( "algorithm: v%d\n", 0 );
|
||||||
|
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
|
||||||
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
|
// 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( "algorithm: v%d\n", 0 );
|
||||||
|
trc( "siteName: %s\n", siteName );
|
||||||
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
|
trc( "siteType: %d\n", siteType );
|
||||||
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||||
|
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||||
|
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||||
|
mpw_hex_l( htonl( siteCounter ) ),
|
||||||
|
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||||
|
|
||||||
|
// Calculate the site seed.
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
116
MasterPassword/C/mpw-algorithm_v1.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
//
|
||||||
|
// 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-types.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( "algorithm: v%d\n", 1 );
|
||||||
|
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
|
||||||
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
|
// 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( "algorithm: v%d\n", 1 );
|
||||||
|
trc( "siteName: %s\n", siteName );
|
||||||
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
|
trc( "siteType: %d\n", siteType );
|
||||||
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||||
|
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||||
|
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||||
|
mpw_hex_l( htonl( siteCounter ) ),
|
||||||
|
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||||
|
|
||||||
|
// Calculate the site seed.
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
116
MasterPassword/C/mpw-algorithm_v2.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
//
|
||||||
|
// 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-types.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( "algorithm: v%d\n", 2 );
|
||||||
|
trc( "fullName: %s (%zu)\n", fullName, mpw_charlen( fullName ) );
|
||||||
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
|
// 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( "algorithm: v%d\n", 2 );
|
||||||
|
trc( "siteName: %s\n", siteName );
|
||||||
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
|
trc( "siteType: %d\n", siteType );
|
||||||
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||||
|
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||||
|
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||||
|
mpw_hex_l( htonl( siteCounter ) ),
|
||||||
|
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||||
|
|
||||||
|
// Calculate the site seed.
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
116
MasterPassword/C/mpw-algorithm_v3.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
//
|
||||||
|
// 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-types.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( "algorithm: v%d\n", 3 );
|
||||||
|
trc( "fullName: %s (%zu)\n", fullName, strlen( 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( "algorithm: v%d\n", 3 );
|
||||||
|
trc( "siteName: %s\n", siteName );
|
||||||
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
|
trc( "siteType: %d\n", siteType );
|
||||||
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext? "<empty>": siteContext );
|
||||||
|
trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n",
|
||||||
|
siteScope, mpw_hex_l( htonl( strlen( siteName ) ) ), siteName,
|
||||||
|
mpw_hex_l( htonl( siteCounter ) ),
|
||||||
|
mpw_hex_l( htonl( siteContext? strlen( siteContext ): 0 ) ), siteContext? "(null)": siteContext );
|
||||||
|
|
||||||
|
// Calculate the site seed.
|
||||||
|
// 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,17 +15,17 @@
|
|||||||
|
|
||||||
#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() {
|
||||||
|
|
||||||
fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] site\n\n" );
|
fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] [-V version] [-v variant] [-C context] [-h] site\n\n" );
|
||||||
fprintf( stderr, " -u name Specify the full name of the user.\n"
|
fprintf( stderr, " -u name Specify the full name of the user.\n"
|
||||||
" Defaults to %s in env.\n\n", MP_env_fullname );
|
" Defaults to %s in env.\n\n", MP_env_fullname );
|
||||||
fprintf( stderr, " -t type Specify the password's template.\n"
|
fprintf( stderr, " -t type Specify the password's template.\n"
|
||||||
@@ -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"
|
||||||
|
|
||||||
@@ -22,6 +21,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// Read in the test case.
|
// Read in the test case.
|
||||||
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
|
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
|
||||||
|
uint32_t algorithm = mpw_xmlTestCaseInteger( testCase, "algorithm" );
|
||||||
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
|
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
|
||||||
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
|
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
|
||||||
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
|
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
|
||||||
@@ -37,16 +37,20 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
// Run the test case.
|
// Run the test case.
|
||||||
fprintf( stdout, "test case %s... ", id );
|
fprintf( stdout, "test case %s... ", id );
|
||||||
|
if (!xmlStrlen( result )) {
|
||||||
|
fprintf( stdout, "abstract.\n" );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. calculate the master key.
|
// 1. calculate the master key.
|
||||||
const uint8_t *masterKey = mpw_masterKeyForUser(
|
const uint8_t *masterKey = mpw_masterKeyForUser(
|
||||||
(char *)fullName, (char *)masterPassword );
|
(char *)fullName, (char *)masterPassword, algorithm );
|
||||||
if (!masterKey)
|
if (!masterKey)
|
||||||
ftl( "Couldn't derive master key." );
|
ftl( "Couldn't derive master key." );
|
||||||
|
|
||||||
// 2. calculate the site password.
|
// 2. calculate the site password.
|
||||||
const char *sitePassword = mpw_passwordForSite(
|
const char *sitePassword = mpw_passwordForSite(
|
||||||
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext );
|
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, algorithm );
|
||||||
mpw_free( masterKey, MP_dkLen );
|
mpw_free( masterKey, MP_dkLen );
|
||||||
if (!sitePassword)
|
if (!sitePassword)
|
||||||
ftl( "Couldn't derive site password." );
|
ftl( "Couldn't derive site password." );
|
||||||
@@ -57,7 +61,7 @@ int main(int argc, char *const argv[]) {
|
|||||||
|
|
||||||
else {
|
else {
|
||||||
++failedTests;
|
++failedTests;
|
||||||
fprintf( stdout, "FAILED! (result %s != expected %s)\n", result, sitePassword );
|
fprintf( stdout, "FAILED! (got %s != expected %s)\n", sitePassword, result );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free test case.
|
// Free test case.
|
||||||
|
|||||||
@@ -21,15 +21,17 @@
|
|||||||
|
|
||||||
const MPSiteType mpw_typeWithName(const char *typeName) {
|
const MPSiteType mpw_typeWithName(const char *typeName) {
|
||||||
|
|
||||||
|
// Lower-case and trim optionally leading "Generated" string from typeName to standardize it.
|
||||||
|
size_t stdTypeNameOffset = 0;
|
||||||
size_t stdTypeNameSize = strlen( typeName );
|
size_t stdTypeNameSize = strlen( typeName );
|
||||||
char stdTypeName[strlen( typeName )];
|
if (strstr(typeName, "Generated" ) == typeName)
|
||||||
if (stdTypeNameSize > strlen( "generated" ))
|
stdTypeNameSize -= (stdTypeNameOffset = strlen( "Generated" ));
|
||||||
strcpy( stdTypeName, typeName + strlen( "generated" ) );
|
char stdTypeName[stdTypeNameSize + 1];
|
||||||
else
|
for (size_t c = 0; c < stdTypeNameSize; ++c)
|
||||||
strcpy( stdTypeName, typeName );
|
stdTypeName[c] = (char)tolower( typeName[c + stdTypeNameOffset] );
|
||||||
for (char *tN = stdTypeName; *tN; ++tN)
|
stdTypeName[stdTypeNameSize] = '\0';
|
||||||
*tN = (char)tolower( *tN );
|
|
||||||
|
|
||||||
|
// Find what site type is represented by the type name.
|
||||||
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
|
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
|
||||||
return MPSiteTypeGeneratedMaximum;
|
return MPSiteTypeGeneratedMaximum;
|
||||||
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
|
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
|
||||||
@@ -47,66 +49,84 @@ const MPSiteType mpw_typeWithName(const char *typeName) {
|
|||||||
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
|
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
|
||||||
return MPSiteTypeGeneratedPhrase;
|
return MPSiteTypeGeneratedPhrase;
|
||||||
|
|
||||||
fprintf( stderr, "Not a generated type name: %s", stdTypeName );
|
ftl( "Not a generated type name: %s", stdTypeName );
|
||||||
abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
|
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" };
|
return alloc_array( *count, const char *,
|
||||||
return templates[seedByte % 2];
|
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedLong: {
|
case MPSiteTypeGeneratedLong: {
|
||||||
const char *templates[] = { "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
return alloc_array( *count, const char *,
|
||||||
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
||||||
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
||||||
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
||||||
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
||||||
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
||||||
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" };
|
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
||||||
return templates[seedByte % 21];
|
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedMedium: {
|
case MPSiteTypeGeneratedMedium: {
|
||||||
const char *templates[] = { "CvcnoCvc", "CvcCvcno" };
|
return alloc_array( *count, const char *,
|
||||||
return templates[seedByte % 2];
|
"CvcnoCvc", "CvcCvcno" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedBasic: {
|
case MPSiteTypeGeneratedBasic: {
|
||||||
const char *templates[] = { "aaanaaan", "aannaaan", "aaannaaa" };
|
return alloc_array( *count, const char *,
|
||||||
return templates[seedByte % 3];
|
"aaanaaan", "aannaaan", "aaannaaa" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedShort: {
|
case MPSiteTypeGeneratedShort: {
|
||||||
return "Cvcn";
|
return alloc_array( *count, const char *,
|
||||||
|
"Cvcn" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedPIN: {
|
case MPSiteTypeGeneratedPIN: {
|
||||||
return "nnnn";
|
return alloc_array( *count, const char *,
|
||||||
|
"nnnn" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedName: {
|
case MPSiteTypeGeneratedName: {
|
||||||
return "cvccvcvcv";
|
return alloc_array( *count, const char *,
|
||||||
|
"cvccvcvcv" );
|
||||||
}
|
}
|
||||||
case MPSiteTypeGeneratedPhrase: {
|
case MPSiteTypeGeneratedPhrase: {
|
||||||
const char *templates[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
|
return alloc_array( *count, const char *,
|
||||||
return templates[seedByte % 3];
|
"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;
|
||||||
|
|
||||||
|
char const *template = templates[seedByte % count];
|
||||||
|
free( templates );
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
const MPSiteVariant mpw_variantWithName(const char *variantName) {
|
const MPSiteVariant mpw_variantWithName(const char *variantName) {
|
||||||
|
|
||||||
char stdVariantName[strlen( variantName )];
|
// Lower-case and trim optionally leading "generated" string from typeName to standardize it.
|
||||||
strcpy( stdVariantName, variantName );
|
size_t stdVariantNameSize = strlen( variantName );
|
||||||
for (char *vN = stdVariantName; *vN; ++vN)
|
char stdVariantName[stdVariantNameSize + 1];
|
||||||
*vN = (char)tolower( *vN );
|
for (size_t c = 0; c < stdVariantNameSize; ++c)
|
||||||
|
stdVariantName[c] = (char)tolower( variantName[c] );
|
||||||
|
stdVariantName[stdVariantNameSize] = '\0';
|
||||||
|
|
||||||
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
|
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
|
||||||
return MPSiteVariantPassword;
|
return MPSiteVariantPassword;
|
||||||
@@ -138,55 +158,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,45 @@
|
|||||||
// Copyright (c) 2014 Lyndir. All rights reserved.
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#ifndef _MPW_TYPES_H
|
||||||
|
#define _MPW_TYPES_H
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <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 +56,44 @@ 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 A newly allocated array of internal strings that express the templates to use for the given type.
|
||||||
|
* The amount of elements in the array is stored in count.
|
||||||
|
* If an unsupported type is given, count will be 0 and will return NULL.
|
||||||
|
* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
|
||||||
|
*/
|
||||||
|
const char **mpw_templatesForType(MPSiteType type, size_t *count);
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
#endif // _MPW_TYPES_H
|
||||||
|
|||||||
@@ -10,6 +10,12 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef COLOR
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <curses.h>
|
||||||
|
#include <term.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <scrypt/sha256.h>
|
#include <scrypt/sha256.h>
|
||||||
#include <scrypt/crypto_scrypt.h>
|
#include <scrypt/crypto_scrypt.h>
|
||||||
|
|
||||||
@@ -74,7 +80,7 @@ uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_
|
|||||||
|
|
||||||
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
|
uint8_t const *mpw_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize) {
|
||||||
|
|
||||||
uint8_t *const buffer = malloc(32);
|
uint8_t *const buffer = malloc( 32 );
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -90,20 +96,35 @@ const char *mpw_idForBuf(const void *buf, size_t length) {
|
|||||||
return mpw_hex( hash, 32 );
|
return mpw_hex( hash, 32 );
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *mpw_hex_buf = NULL;
|
static char **mpw_hex_buf = NULL;
|
||||||
|
static unsigned int mpw_hex_buf_i = 0;
|
||||||
|
|
||||||
const char *mpw_hex(const void *buf, size_t length) {
|
const char *mpw_hex(const void *buf, size_t length) {
|
||||||
|
|
||||||
mpw_hex_buf = realloc( mpw_hex_buf, length * 2 + 1 );
|
// FIXME
|
||||||
for (size_t kH = 0; kH < length; kH++)
|
if (!mpw_hex_buf) {
|
||||||
sprintf( &(mpw_hex_buf[kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
|
mpw_hex_buf = malloc( 10 * sizeof( char * ) );
|
||||||
|
for (uint8_t i = 0; i < 10; ++i)
|
||||||
|
mpw_hex_buf[i] = NULL;
|
||||||
|
}
|
||||||
|
mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10;
|
||||||
|
|
||||||
return mpw_hex_buf;
|
mpw_hex_buf[mpw_hex_buf_i] = realloc( mpw_hex_buf[mpw_hex_buf_i], length * 2 + 1 );
|
||||||
|
for (size_t kH = 0; kH < length; kH++)
|
||||||
|
sprintf( &(mpw_hex_buf[mpw_hex_buf_i][kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
|
||||||
|
|
||||||
|
return mpw_hex_buf[mpw_hex_buf_i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mpw_hex_l(uint32_t number) {
|
||||||
|
|
||||||
|
return mpw_hex( &number, sizeof( number ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef COLOR
|
#ifdef COLOR
|
||||||
static int putvari;
|
static int putvari;
|
||||||
static char *putvarc = NULL;
|
static char *putvarc = NULL;
|
||||||
static bool istermsetup = false;
|
static int istermsetup = 0;
|
||||||
static void initputvar() {
|
static void initputvar() {
|
||||||
if (putvarc)
|
if (putvarc)
|
||||||
free(putvarc);
|
free(putvarc);
|
||||||
@@ -127,7 +148,8 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
|||||||
const char *accessory[] = {
|
const char *accessory[] = {
|
||||||
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
||||||
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
||||||
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌" };
|
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌"
|
||||||
|
};
|
||||||
|
|
||||||
uint8_t identiconSeed[32];
|
uint8_t identiconSeed[32];
|
||||||
HMAC_SHA256_Buf( masterPassword, strlen( masterPassword ), fullName, strlen( fullName ), identiconSeed );
|
HMAC_SHA256_Buf( masterPassword, strlen( masterPassword ), fullName, strlen( fullName ), identiconSeed );
|
||||||
@@ -163,3 +185,34 @@ const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
|||||||
free( resetString );
|
free( resetString );
|
||||||
return identicon;
|
return identicon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
|
||||||
|
*/
|
||||||
|
static int mpw_charByteSize(unsigned char utf8Byte) {
|
||||||
|
|
||||||
|
if (!utf8Byte)
|
||||||
|
return 0;
|
||||||
|
if ((utf8Byte & 0x80) == 0)
|
||||||
|
return 1;
|
||||||
|
if ((utf8Byte & 0xC0) != 0xC0)
|
||||||
|
return 0;
|
||||||
|
if ((utf8Byte & 0xE0) == 0xC0)
|
||||||
|
return 2;
|
||||||
|
if ((utf8Byte & 0xF0) == 0xE0)
|
||||||
|
return 3;
|
||||||
|
if ((utf8Byte & 0xF8) == 0xF0)
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t mpw_charlen(const char *utf8String) {
|
||||||
|
|
||||||
|
size_t charlen = 0;
|
||||||
|
char *remainingString = (char *)utf8String;
|
||||||
|
for (int charByteSize; (charByteSize = mpw_charByteSize( (unsigned char)*remainingString )); remainingString += charByteSize)
|
||||||
|
++charlen;
|
||||||
|
|
||||||
|
return charlen;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,19 +6,33 @@
|
|||||||
// 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
|
||||||
|
|
||||||
//// Buffers and memory.
|
//// Buffers and memory.
|
||||||
|
|
||||||
|
#define alloc_array(_count, _type, ...) ({ \
|
||||||
|
_type stackElements[] = { __VA_ARGS__ }; \
|
||||||
|
_count = sizeof( stackElements ) / sizeof( _type ); \
|
||||||
|
_type *allocElements = malloc( sizeof( stackElements ) ); \
|
||||||
|
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
|
||||||
|
allocElements; \
|
||||||
|
})
|
||||||
|
|
||||||
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
|
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
|
||||||
void mpw_pushBuf(
|
void mpw_pushBuf(
|
||||||
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
|
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
|
||||||
@@ -50,11 +64,17 @@ 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);
|
||||||
|
const char *mpw_hex_l(uint32_t number);
|
||||||
/** 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.
|
||||||
|
|
||||||
|
/** @return The amount of display characters in the given UTF-8 string. */
|
||||||
|
const size_t mpw_charlen(const char *utf8String);
|
||||||
|
|||||||
69
MasterPassword/C/mpw.completion.bash
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
source bashcomplib
|
||||||
|
|
||||||
|
# completing the 'mpw' command.
|
||||||
|
_comp_mpw() {
|
||||||
|
local optarg= cword=${COMP_WORDS[COMP_CWORD]} pcword=
|
||||||
|
(( COMP_CWORD )) && pcword=${COMP_WORDS[COMP_CWORD - 1]}
|
||||||
|
|
||||||
|
case $pcword in
|
||||||
|
-u) # complete full names.
|
||||||
|
COMPREPLY=( ~/.mpw.d/*.mpsites )
|
||||||
|
[[ -e $COMPREPLY ]] || COMPREPLY=()
|
||||||
|
COMPREPLY=( "${COMPREPLY[@]##*/}" ) COMPREPLY=( "${COMPREPLY[@]%.mpsites}" )
|
||||||
|
;;
|
||||||
|
-t) # complete types.
|
||||||
|
COMPREPLY=( maximum long medium basic short pin name phrase )
|
||||||
|
;;
|
||||||
|
-c) # complete counter.
|
||||||
|
COMPREPLY=( 1 )
|
||||||
|
;;
|
||||||
|
-V) # complete versions.
|
||||||
|
COMPREPLY=( 0 1 2 3 )
|
||||||
|
;;
|
||||||
|
-v) # complete variants.
|
||||||
|
COMPREPLY=( password login answer )
|
||||||
|
;;
|
||||||
|
-C) # complete context.
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# previous word is not an option we can complete, complete site name (or option if leading -)
|
||||||
|
if [[ $cword = -* ]]; then
|
||||||
|
COMPREPLY=( -u -t -c -V -v -C )
|
||||||
|
else
|
||||||
|
local w fullName=$MP_FULLNAME
|
||||||
|
for (( w = 0; w < ${#COMP_WORDS[@]}; ++w )); do
|
||||||
|
[[ ${COMP_WORDS[w]} = -u ]] && fullName=$(xargs <<< "${COMP_WORDS[w + 1]}") && break
|
||||||
|
done
|
||||||
|
if [[ -e ~/.mpw.d/"$fullName.mpsites" ]]; then
|
||||||
|
IFS=$'\n' read -d '' -ra COMPREPLY < <(awk -F$'\t' '!/^ *#/{sub(/^ */, "", $2); print $2}' ~/.mpw.d/"$fullName.mpsites")
|
||||||
|
printf -v _comp_title 'Sites for %s' "$fullName"
|
||||||
|
else
|
||||||
|
# Default list from the Alexa Top 500
|
||||||
|
COMPREPLY=(
|
||||||
|
163.com 360.cn 9gag.com adobe.com alibaba.com aliexpress.com amazon.com
|
||||||
|
apple.com archive.org ask.com baidu.com battle.net booking.com buzzfeed.com
|
||||||
|
chase.com cnn.com comcast.net craigslist.org dailymotion.com dell.com
|
||||||
|
deviantart.com diply.com disqus.com dropbox.com ebay.com engadget.com
|
||||||
|
espn.go.com evernote.com facebook.com fedex.com feedly.com flickr.com
|
||||||
|
flipkart.com github.com gizmodo.com go.com goodreads.com google.com
|
||||||
|
huffingtonpost.com hulu.com ign.com ikea.com imdb.com imgur.com
|
||||||
|
indiatimes.com instagram.com jd.com kickass.to kickstarter.com linkedin.com
|
||||||
|
live.com livedoor.com mail.ru mozilla.org naver.com netflix.com newegg.com
|
||||||
|
nicovideo.jp nytimes.com pandora.com paypal.com pinterest.com pornhub.com
|
||||||
|
qq.com rakuten.co.jp reddit.com redtube.com shutterstock.com skype.com
|
||||||
|
soso.com spiegel.de spotify.com stackexchange.com steampowered.com
|
||||||
|
stumbleupon.com taobao.com target.com thepiratebay.se tmall.com
|
||||||
|
torrentz.eu tripadvisor.com tube8.com tubecup.com tudou.com tumblr.com
|
||||||
|
twitter.com uol.com.br vimeo.com vk.com walmart.com weibo.com whatsapp.com
|
||||||
|
wikia.com wikipedia.org wired.com wordpress.com xhamster.com xinhuanet.com
|
||||||
|
xvideos.com yahoo.com yandex.ru yelp.com youku.com youporn.com ziddu.com
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
fi ;;
|
||||||
|
esac
|
||||||
|
_comp_finish_completions
|
||||||
|
}
|
||||||
|
|
||||||
|
#complete -F _show_args mpw
|
||||||
|
complete -o nospace -F _comp_mpw mpw
|
||||||
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.3</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 -->
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.nio.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 15-03-29
|
||||||
|
*/
|
||||||
|
public class MPIdenticon {
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MPIdenticon.class );
|
||||||
|
|
||||||
|
private static final Charset charset = StandardCharsets.UTF_8;
|
||||||
|
private static final Color[] colors = new Color[]{
|
||||||
|
Color.RED, Color.GREEN, Color.YELLOW, Color.BLUE, Color.MAGENTA, Color.CYAN, Color.MONO };
|
||||||
|
private static final char[] leftArm = new char[]{ '╔', '╚', '╰', '═' };
|
||||||
|
private static final char[] rightArm = new char[]{ '╗', '╝', '╯', '═' };
|
||||||
|
private static final char[] body = new char[]{ '█', '░', '▒', '▓', '☺', '☻' };
|
||||||
|
private static final char[] accessory = new char[]{
|
||||||
|
'◈', '◎', '◐', '◑', '◒', '◓', '☀', '☁', '☂', '☃', '☄', '★', '☆', '☎', '☏', '⎈', '⌂', '☘', '☢', '☣', '☕', '⌚', '⌛', '⏰', '⚡',
|
||||||
|
'⛄', '⛅', '☔', '♔', '♕', '♖', '♗', '♘', '♙', '♚', '♛', '♜', '♝', '♞', '♟', '♨', '♩', '♪', '♫', '⚐', '⚑', '⚔', '⚖', '⚙', '⚠',
|
||||||
|
'⌘', '⏎', '✄', '✆', '✈', '✉', '✌' };
|
||||||
|
|
||||||
|
private final String fullName;
|
||||||
|
private final Color color;
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
public MPIdenticon(String fullName, String masterPassword) {
|
||||||
|
this( fullName, masterPassword.toCharArray() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public MPIdenticon(String fullName, char[] masterPassword) {
|
||||||
|
this.fullName = fullName;
|
||||||
|
|
||||||
|
byte[] masterPasswordBytes = charset.encode( CharBuffer.wrap( masterPassword ) ).array();
|
||||||
|
ByteBuffer identiconSeedBytes = ByteBuffer.wrap(
|
||||||
|
MessageAuthenticationDigests.HmacSHA256.of( masterPasswordBytes, fullName.getBytes( charset ) ) );
|
||||||
|
Arrays.fill( masterPasswordBytes, (byte) 0 );
|
||||||
|
|
||||||
|
IntBuffer identiconSeedBuffer = IntBuffer.allocate( identiconSeedBytes.capacity() );
|
||||||
|
while (identiconSeedBytes.hasRemaining())
|
||||||
|
identiconSeedBuffer.put( identiconSeedBytes.get() & 0xFF );
|
||||||
|
int[] identiconSeed = identiconSeedBuffer.array();
|
||||||
|
|
||||||
|
color = colors[identiconSeed[4] % colors.length];
|
||||||
|
text = strf( "%c%c%c%c", leftArm[identiconSeed[0] % leftArm.length], body[identiconSeed[1] % body.length],
|
||||||
|
rightArm[identiconSeed[2] % rightArm.length], accessory[identiconSeed[3] % accessory.length] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullName() {
|
||||||
|
return fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BackgroundMode {
|
||||||
|
DARK, LIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum Color {
|
||||||
|
RED( "#dc322f", "#dc322f" ),
|
||||||
|
GREEN( "#859900", "#859900" ),
|
||||||
|
YELLOW( "#b58900", "#b58900" ),
|
||||||
|
BLUE( "#268bd2", "#268bd2" ),
|
||||||
|
MAGENTA( "#d33682", "#d33682" ),
|
||||||
|
CYAN( "#2aa198", "#2aa198" ),
|
||||||
|
MONO( "#93a1a1", "#586e75" );
|
||||||
|
|
||||||
|
private final String rgbDark;
|
||||||
|
private final String rgbLight;
|
||||||
|
|
||||||
|
Color(final String rgbDark, final String rgbLight) {
|
||||||
|
this.rgbDark = rgbDark;
|
||||||
|
this.rgbLight = rgbLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public java.awt.Color getAWTColor(BackgroundMode backgroundMode) {
|
||||||
|
switch (backgroundMode) {
|
||||||
|
case DARK:
|
||||||
|
return new java.awt.Color( Integer.decode( rgbDark ) );
|
||||||
|
case LIGHT:
|
||||||
|
return new java.awt.Color( Integer.decode( rgbLight ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException( "Unsupported background mode: " + backgroundMode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,11 +144,12 @@ 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) {
|
@Contract("!null -> !null, null -> null")
|
||||||
|
public static MPSiteType forName(@Nullable final String name) {
|
||||||
|
|
||||||
if (name == null)
|
if (name == null)
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.lyndir.masterpassword;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,11 +63,12 @@ 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) {
|
@Contract("!null -> !null, null -> null")
|
||||||
|
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,165 @@
|
|||||||
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() {
|
||||||
|
|
||||||
|
Preconditions.checkState( isValid() );
|
||||||
|
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,146 @@
|
|||||||
|
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 ) ) );
|
||||||
|
|
||||||
|
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
|
||||||
|
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
|
||||||
|
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
||||||
|
Arrays.fill( mpBytesBuf.array(), (byte) 0 );
|
||||||
|
|
||||||
|
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 || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
|
||||||
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
|
||||||
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
|
siteContextBytes == null? "(null)": siteContext );
|
||||||
|
|
||||||
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
|
if (siteContextBytes != null)
|
||||||
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||||
|
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
|
byte[] sitePasswordSeedBytes = MP_mac.of( getKey(), sitePasswordInfo );
|
||||||
|
int[] sitePasswordSeed = new int[sitePasswordSeedBytes.length];
|
||||||
|
for (int i = 0; i < sitePasswordSeedBytes.length; ++i) {
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( ByteOrder.BIG_ENDIAN );
|
||||||
|
Arrays.fill( buf.array(), sitePasswordSeedBytes[i] > 0? (byte)0x00: (byte) 0xFF );
|
||||||
|
buf.position( 2 );
|
||||||
|
buf.put( sitePasswordSeedBytes[i] ).rewind();
|
||||||
|
sitePasswordSeed[i] = buf.getInt() & 0xFFFF;
|
||||||
|
}
|
||||||
|
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeedBytes ) ) );
|
||||||
|
|
||||||
|
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||||
|
int templateIndex = sitePasswordSeed[0];
|
||||||
|
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];
|
||||||
|
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 || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
|
||||||
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
|
||||||
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
|
siteContextBytes == null? "(null)": siteContext );
|
||||||
|
|
||||||
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
|
if (siteContextBytes != null)
|
||||||
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||||
|
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
|
byte[] sitePasswordSeed = MP_mac.of( 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 || siteContext.isEmpty()? null: siteContext.getBytes( MP_charset );
|
||||||
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContextBytes == null? "<empty>": siteContext );
|
||||||
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
|
siteContextBytes == null? "(null)": siteContext );
|
||||||
|
|
||||||
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
|
if (siteContextBytes != null)
|
||||||
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||||
|
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
|
byte[] sitePasswordSeed = MP_mac.of( 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,62 @@
|
|||||||
|
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.ByteBuffer;
|
||||||
|
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 ) ) );
|
||||||
|
|
||||||
|
ByteBuffer mpBytesBuf = MP_charset.encode( CharBuffer.wrap( masterPassword ) );
|
||||||
|
byte[] mpBytes = new byte[mpBytesBuf.remaining()];
|
||||||
|
mpBytesBuf.get( mpBytes, 0, mpBytes.length );
|
||||||
|
Arrays.fill( mpBytesBuf.array(), (byte)0 );
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.lhunath.opal.system.util.NNSupplier;
|
import com.lyndir.lhunath.opal.system.util.*;
|
||||||
import com.lyndir.lhunath.opal.system.util.NSupplier;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -25,8 +25,9 @@ public class MPWTests {
|
|||||||
@XmlElement(name = "case")
|
@XmlElement(name = "case")
|
||||||
private List<Case> cases;
|
private List<Case> cases;
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public List<Case> getCases() {
|
public List<Case> getCases() {
|
||||||
return cases;
|
return checkNotNull( cases );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Case getCase(String identifier) {
|
public Case getCase(String identifier) {
|
||||||
@@ -45,6 +46,8 @@ public class MPWTests {
|
|||||||
@XmlAttribute
|
@XmlAttribute
|
||||||
private String parent;
|
private String parent;
|
||||||
@XmlElement
|
@XmlElement
|
||||||
|
private String algorithm;
|
||||||
|
@XmlElement
|
||||||
private String fullName;
|
private String fullName;
|
||||||
@XmlElement
|
@XmlElement
|
||||||
private String masterPassword;
|
private String masterPassword;
|
||||||
@@ -65,76 +68,86 @@ public class MPWTests {
|
|||||||
|
|
||||||
private transient Case parentCase;
|
private transient Case parentCase;
|
||||||
|
|
||||||
public void setTests(MPWTests tests) {
|
public void initializeParentHierarchy(MPWTests tests) {
|
||||||
|
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
parentCase = tests.getCase( parent );
|
parentCase = tests.getCase( parent );
|
||||||
fullName = ifNotNullElse( fullName, new NNSupplier<String>() {
|
parentCase.initializeParentHierarchy( tests );
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getFullName();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
masterPassword = ifNotNullElse( masterPassword, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getMasterPassword();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getKeyID();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getSiteName();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteCounter = ifNotNullElse( siteCounter, new NNSupplier<Integer>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public Integer get() {
|
|
||||||
return parentCase.getSiteCounter();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getSiteType().name();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getSiteVariant().name();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
siteContext = ifNotNullElseNullable( siteContext, new NSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getSiteContext();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
result = ifNotNullElse( result, new NNSupplier<String>() {
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public String get() {
|
|
||||||
return parentCase.getResult();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
algorithm = ifNotNullElse( algorithm, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.algorithm );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
fullName = ifNotNullElse( fullName, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.fullName );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
masterPassword = ifNotNullElse( masterPassword, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.masterPassword );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.keyID );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.siteName );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteCounter = ifNotNullElse( siteCounter, new NNSupplier<Integer>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Integer get() {
|
||||||
|
return checkNotNull( parentCase.siteCounter );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.siteType );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return checkNotNull( parentCase.siteVariant );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteContext = ifNotNullElse( siteContext, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase == null? "": checkNotNull( parentCase.siteContext );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
result = ifNotNullElse( result, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase == null? "": checkNotNull( parentCase.result );
|
||||||
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getIdentifier() {
|
public String getIdentifier() {
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
@@ -144,40 +157,53 @@ public class MPWTests {
|
|||||||
return parentCase;
|
return parentCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public MasterKey.Version getAlgorithm() {
|
||||||
|
return MasterKey.Version.fromInt( ConversionUtils.toIntegerNN( algorithm ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getFullName() {
|
public String getFullName() {
|
||||||
return fullName;
|
return checkNotNull( fullName );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMasterPassword() {
|
@Nonnull
|
||||||
return masterPassword;
|
public char[] getMasterPassword() {
|
||||||
|
return checkNotNull( masterPassword ).toCharArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getKeyID() {
|
public String getKeyID() {
|
||||||
return keyID;
|
return checkNotNull( keyID );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getSiteName() {
|
public String getSiteName() {
|
||||||
return siteName;
|
return checkNotNull( siteName );
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSiteCounter() {
|
public int getSiteCounter() {
|
||||||
return siteCounter;
|
return ifNotNullElse( siteCounter, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public MPSiteType getSiteType() {
|
public MPSiteType getSiteType() {
|
||||||
return MPSiteType.forName( siteType );
|
return MPSiteType.forName( checkNotNull( siteType ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public MPSiteVariant getSiteVariant() {
|
public MPSiteVariant getSiteVariant() {
|
||||||
return MPSiteVariant.forName( siteVariant );
|
return MPSiteVariant.forName( checkNotNull( siteVariant ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getSiteContext() {
|
public String getSiteContext() {
|
||||||
return siteContext;
|
return checkNotNull( siteContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public String getResult() {
|
public String getResult() {
|
||||||
return result;
|
return checkNotNull( result );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import static org.testng.Assert.*;
|
|||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import com.lyndir.lhunath.opal.system.util.StringUtils;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import javax.xml.bind.JAXBContext;
|
import javax.xml.bind.JAXBContext;
|
||||||
import org.testng.annotations.BeforeMethod;
|
import org.testng.annotations.BeforeMethod;
|
||||||
@@ -26,7 +27,7 @@ public class MasterKeyTest {
|
|||||||
URL testCasesResource = Resources.getResource( "mpw_tests.xml" );
|
URL testCasesResource = Resources.getResource( "mpw_tests.xml" );
|
||||||
tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource );
|
tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource );
|
||||||
for (MPWTests.Case testCase : tests.getCases())
|
for (MPWTests.Case testCase : tests.getCases())
|
||||||
testCase.setTests( tests );
|
testCase.initializeParentHierarchy( tests );
|
||||||
defaultCase = tests.getCase( MPWTests.ID_DEFAULT );
|
defaultCase = tests.getCase( MPWTests.ID_DEFAULT );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,10 +36,15 @@ public class MasterKeyTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
|
|
||||||
for (MPWTests.Case testCase : tests.getCases()) {
|
for (MPWTests.Case testCase : tests.getCases()) {
|
||||||
MasterKey masterKey = new MasterKey( testCase.getFullName(), testCase.getMasterPassword() );
|
if (testCase.getResult().isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
logger.inf( "Running test case: %s [testEncode]", testCase.getIdentifier() );
|
||||||
|
MasterKey masterKey = MasterKey.create( testCase.getAlgorithm(), testCase.getFullName(), testCase.getMasterPassword() );
|
||||||
assertEquals(
|
assertEquals(
|
||||||
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
||||||
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
||||||
|
logger.inf( "passed!" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +52,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,8 +61,13 @@ 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() );
|
if (testCase.getResult().isEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
logger.inf( "Running test case: %s [testGetKeyID]", testCase.getIdentifier() );
|
||||||
|
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
||||||
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
|
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
|
||||||
|
logger.inf( "passed!" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +76,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() );
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<tests>
|
<tests>
|
||||||
|
<!-- Default values for all parameters. -->
|
||||||
<case id="default">
|
<case id="default">
|
||||||
|
<algorithm><!-- current --></algorithm>
|
||||||
<fullName>Robert Lee Mitchell</fullName>
|
<fullName>Robert Lee Mitchell</fullName>
|
||||||
<masterPassword>banana colored duckling</masterPassword>
|
<masterPassword>banana colored duckling</masterPassword>
|
||||||
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
|
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
|
||||||
@@ -7,67 +9,271 @@
|
|||||||
<siteCounter>1</siteCounter>
|
<siteCounter>1</siteCounter>
|
||||||
<siteType>GeneratedLong</siteType>
|
<siteType>GeneratedLong</siteType>
|
||||||
<siteVariant>Password</siteVariant>
|
<siteVariant>Password</siteVariant>
|
||||||
|
<result><!-- abstract --></result>
|
||||||
|
</case>
|
||||||
|
|
||||||
|
<!-- Algorithm 3 -->
|
||||||
|
<case id="v3" parent="default">
|
||||||
|
<algorithm>3</algorithm>
|
||||||
<result>Jejr5[RepuSosp</result>
|
<result>Jejr5[RepuSosp</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="mb_fullName" parent="default">
|
<case id="v3_mb_fullName" parent="v3">
|
||||||
<fullName>⛄</fullName>
|
<fullName>⛄</fullName>
|
||||||
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
<result>NopaDajh8=Fene</result>
|
<result>NopaDajh8=Fene</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="mb_masterPassword" parent="default">
|
<case id="v3_mb_masterPassword" parent="v3">
|
||||||
<masterPassword>⛄</masterPassword>
|
<masterPassword>⛄</masterPassword>
|
||||||
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
<result>QesuHirv5-Xepl</result>
|
<result>QesuHirv5-Xepl</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="mb_siteName" parent="default">
|
<case id="v3_mb_siteName" parent="v3">
|
||||||
<siteName>⛄</siteName>
|
<siteName>⛄</siteName>
|
||||||
<result>LiheCuwhSerz6)</result>
|
<result>LiheCuwhSerz6)</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="loginName" parent="default">
|
<case id="v3_loginName" parent="v3">
|
||||||
<siteVariant>Login</siteVariant>
|
<siteVariant>Login</siteVariant>
|
||||||
<siteType>GeneratedName</siteType>
|
<siteType>GeneratedName</siteType>
|
||||||
<result>wohzaqage</result>
|
<result>wohzaqage</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="securityAnswer" parent="default">
|
<case id="v3_securityAnswer" parent="v3">
|
||||||
<siteVariant>Answer</siteVariant>
|
<siteVariant>Answer</siteVariant>
|
||||||
<siteType>GeneratedPhrase</siteType>
|
<siteType>GeneratedPhrase</siteType>
|
||||||
<result>xin diyjiqoja hubu</result>
|
<result>xin diyjiqoja hubu</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="securityAnswer_context" parent="securityAnswer">
|
<case id="v3_securityAnswer_context" parent="v3_securityAnswer">
|
||||||
<siteContext>question</siteContext>
|
<siteContext>question</siteContext>
|
||||||
<result>xogx tem cegyiva jab</result>
|
<result>xogx tem cegyiva jab</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_maximum" parent="default">
|
<case id="v3_type_maximum" parent="v3">
|
||||||
<siteType>GeneratedMaximum</siteType>
|
<siteType>GeneratedMaximum</siteType>
|
||||||
<result>W6@692^B1#&@gVdSdLZ@</result>
|
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_medium" parent="default">
|
<case id="v3_type_medium" parent="v3">
|
||||||
<siteType>GeneratedMedium</siteType>
|
<siteType>GeneratedMedium</siteType>
|
||||||
<result>Jej2$Quv</result>
|
<result>Jej2$Quv</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_basic" parent="default">
|
<case id="v3_type_basic" parent="v3">
|
||||||
<siteType>GeneratedBasic</siteType>
|
<siteType>GeneratedBasic</siteType>
|
||||||
<result>WAo2xIg6</result>
|
<result>WAo2xIg6</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_short" parent="default">
|
<case id="v3_type_short" parent="v3">
|
||||||
<siteType>GeneratedShort</siteType>
|
<siteType>GeneratedShort</siteType>
|
||||||
<result>Jej2</result>
|
<result>Jej2</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_pin" parent="default">
|
<case id="v3_type_pin" parent="v3">
|
||||||
<siteType>GeneratedPIN</siteType>
|
<siteType>GeneratedPIN</siteType>
|
||||||
<result>7662</result>
|
<result>7662</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_name" parent="default">
|
<case id="v3_type_name" parent="v3">
|
||||||
<siteType>GeneratedName</siteType>
|
<siteType>GeneratedName</siteType>
|
||||||
<result>jejraquvo</result>
|
<result>jejraquvo</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="type_phrase" parent="default">
|
<case id="v3_type_phrase" parent="v3">
|
||||||
<siteType>GeneratedPhrase</siteType>
|
<siteType>GeneratedPhrase</siteType>
|
||||||
<result>jejr quv cabsibu tam</result>
|
<result>jejr quv cabsibu tam</result>
|
||||||
</case>
|
</case>
|
||||||
<case id="counter_ceiling" parent="default">
|
<case id="v3_counter_ceiling" parent="v3">
|
||||||
<siteCounter>4294967295</siteCounter>
|
<siteCounter>4294967295</siteCounter>
|
||||||
<result>XambHoqo6[Peni</result>
|
<result>XambHoqo6[Peni</result>
|
||||||
</case>
|
</case>
|
||||||
|
|
||||||
|
<!-- Algorithm 2 -->
|
||||||
|
<case id="v2" parent="default">
|
||||||
|
<algorithm>2</algorithm>
|
||||||
|
<result>Jejr5[RepuSosp</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_mb_fullName" parent="v2">
|
||||||
|
<fullName>⛄</fullName>
|
||||||
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
|
<result>WaqoGuho2[Xaxw</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_mb_masterPassword" parent="v2">
|
||||||
|
<masterPassword>⛄</masterPassword>
|
||||||
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
|
<result>QesuHirv5-Xepl</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_mb_siteName" parent="v2">
|
||||||
|
<siteName>⛄</siteName>
|
||||||
|
<result>LiheCuwhSerz6)</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_loginName" parent="v2">
|
||||||
|
<siteVariant>Login</siteVariant>
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>wohzaqage</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_securityAnswer" parent="v2">
|
||||||
|
<siteVariant>Answer</siteVariant>
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>xin diyjiqoja hubu</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
|
||||||
|
<siteContext>question</siteContext>
|
||||||
|
<result>xogx tem cegyiva jab</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_maximum" parent="v2">
|
||||||
|
<siteType>GeneratedMaximum</siteType>
|
||||||
|
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_medium" parent="v2">
|
||||||
|
<siteType>GeneratedMedium</siteType>
|
||||||
|
<result>Jej2$Quv</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_basic" parent="v2">
|
||||||
|
<siteType>GeneratedBasic</siteType>
|
||||||
|
<result>WAo2xIg6</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_short" parent="v2">
|
||||||
|
<siteType>GeneratedShort</siteType>
|
||||||
|
<result>Jej2</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_pin" parent="v2">
|
||||||
|
<siteType>GeneratedPIN</siteType>
|
||||||
|
<result>7662</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_name" parent="v2">
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>jejraquvo</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_type_phrase" parent="v2">
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>jejr quv cabsibu tam</result>
|
||||||
|
</case>
|
||||||
|
<case id="v2_counter_ceiling" parent="v2">
|
||||||
|
<siteCounter>4294967295</siteCounter>
|
||||||
|
<result>XambHoqo6[Peni</result>
|
||||||
|
</case>
|
||||||
|
|
||||||
|
<!-- Algorithm 1 -->
|
||||||
|
<case id="v1" parent="default">
|
||||||
|
<algorithm>1</algorithm>
|
||||||
|
<result>Jejr5[RepuSosp</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_mb_fullName" parent="v1">
|
||||||
|
<fullName>⛄</fullName>
|
||||||
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
|
<result>WaqoGuho2[Xaxw</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_mb_masterPassword" parent="v1">
|
||||||
|
<masterPassword>⛄</masterPassword>
|
||||||
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
|
<result>QesuHirv5-Xepl</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_mb_siteName" parent="v1">
|
||||||
|
<siteName>⛄</siteName>
|
||||||
|
<result>WawiYarp2@Kodh</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_loginName" parent="v1">
|
||||||
|
<siteVariant>Login</siteVariant>
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>wohzaqage</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_securityAnswer" parent="v1">
|
||||||
|
<siteVariant>Answer</siteVariant>
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>xin diyjiqoja hubu</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
|
||||||
|
<siteContext>question</siteContext>
|
||||||
|
<result>xogx tem cegyiva jab</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_maximum" parent="v1">
|
||||||
|
<siteType>GeneratedMaximum</siteType>
|
||||||
|
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_medium" parent="v1">
|
||||||
|
<siteType>GeneratedMedium</siteType>
|
||||||
|
<result>Jej2$Quv</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_basic" parent="v1">
|
||||||
|
<siteType>GeneratedBasic</siteType>
|
||||||
|
<result>WAo2xIg6</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_short" parent="v1">
|
||||||
|
<siteType>GeneratedShort</siteType>
|
||||||
|
<result>Jej2</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_pin" parent="v1">
|
||||||
|
<siteType>GeneratedPIN</siteType>
|
||||||
|
<result>7662</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_name" parent="v1">
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>jejraquvo</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_type_phrase" parent="v1">
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>jejr quv cabsibu tam</result>
|
||||||
|
</case>
|
||||||
|
<case id="v1_counter_ceiling" parent="v1">
|
||||||
|
<siteCounter>4294967295</siteCounter>
|
||||||
|
<result>XambHoqo6[Peni</result>
|
||||||
|
</case>
|
||||||
|
|
||||||
|
<!-- Algorithm 0 -->
|
||||||
|
<case id="v0" parent="default">
|
||||||
|
<algorithm>0</algorithm>
|
||||||
|
<result>Feji5@ReduWosh</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_mb_fullName" parent="v0">
|
||||||
|
<fullName>⛄</fullName>
|
||||||
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
|
<result>HajrYudo7@Mamh</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_mb_masterPassword" parent="v0">
|
||||||
|
<masterPassword>⛄</masterPassword>
|
||||||
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
|
<result>MewmDini0]Meho</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_mb_siteName" parent="v0">
|
||||||
|
<siteName>⛄</siteName>
|
||||||
|
<result>HahiVana2@Nole</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_loginName" parent="v0">
|
||||||
|
<siteVariant>Login</siteVariant>
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>lozwajave</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_securityAnswer" parent="v0">
|
||||||
|
<siteVariant>Answer</siteVariant>
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>miy lirfijoja dubu</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
|
||||||
|
<siteContext>question</siteContext>
|
||||||
|
<result>movm bex gevrica jaf</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_maximum" parent="v0">
|
||||||
|
<siteType>GeneratedMaximum</siteType>
|
||||||
|
<result>w1!3bA3icmRAc)SS@lwl</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_medium" parent="v0">
|
||||||
|
<siteType>GeneratedMedium</siteType>
|
||||||
|
<result>Fej7]Jug</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_basic" parent="v0">
|
||||||
|
<siteType>GeneratedBasic</siteType>
|
||||||
|
<result>wvH7irC1</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_short" parent="v0">
|
||||||
|
<siteType>GeneratedShort</siteType>
|
||||||
|
<result>Fej7</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_pin" parent="v0">
|
||||||
|
<siteType>GeneratedPIN</siteType>
|
||||||
|
<result>2117</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_name" parent="v0">
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>fejrajugo</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_type_phrase" parent="v0">
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>fejr jug gabsibu bax</result>
|
||||||
|
</case>
|
||||||
|
<case id="v0_counter_ceiling" parent="v0">
|
||||||
|
<siteCounter>4294967295</siteCounter>
|
||||||
|
<result>QateDojh1@Hecn</result>
|
||||||
|
</case>
|
||||||
</tests>
|
</tests>
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<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="2003"
|
||||||
android:versionName="GIT-SNAPSHOT">
|
android:versionName="2.3">
|
||||||
|
|
||||||
<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" />
|
||||||
|
|||||||
13
MasterPassword/Java/masterpassword-android/README
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
To build this module, please ensure you've done the following setup:
|
||||||
|
|
||||||
|
1. Installed the Android SDK and fully downloaded the Android SDK platform 21 in it.
|
||||||
|
2. Set the environment variable ANDROID_HOME in your shell or in ~/.mavenrc to point to the root of your Android SDK install.
|
||||||
|
3. Installed the Android SDK into your Maven's local repository.
|
||||||
|
3a. Clone the maven-android-sdk-deployer available from here: https://github.com/mosabua/maven-android-sdk-deployer.git
|
||||||
|
3b. In the root of this project, run: mvn install -P 5.0
|
||||||
|
|
||||||
|
To build this module:
|
||||||
|
|
||||||
|
1. Build the parent, by going into 'MasterPassword/Java' and running: mvn clean install
|
||||||
|
2. Build this module, by going into 'MasterPassword/Java/masterpassword-android' and running: mvn clean install
|
||||||
|
3. You can then find the APK in: 'MasterPassword/Java/masterpassword-android/target'
|
||||||
@@ -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.3</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.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -103,22 +115,21 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-android</artifactId>
|
<artifactId>slf4j-android</artifactId>
|
||||||
|
<version>1.7.13-underscore</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- clone https://github.com/mosabua/maven-android-sdk-deployer.git
|
|
||||||
run mvn install -P 4.4 -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>android</groupId>
|
<groupId>android</groupId>
|
||||||
<artifactId>android</artifactId>
|
<artifactId>android</artifactId>
|
||||||
|
<version>5.0.1_r2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<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">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
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>
|
||||||
|
|||||||