Compare commits
92 Commits
windows
...
2.2-androi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73372f507a | ||
|
|
feddd038e3 | ||
|
|
3f6773f3a9 | ||
|
|
12b1610dc7 | ||
|
|
e20b33a051 | ||
|
|
b84ae532f2 | ||
|
|
145008406d | ||
|
|
a6ab9b9194 | ||
|
|
78c593fc08 | ||
|
|
5b08149ca6 | ||
|
|
58afc19c26 | ||
|
|
b3109187e9 | ||
|
|
a6e7a749bf | ||
|
|
ca5d83d40c | ||
|
|
285813324f | ||
|
|
d4b772b467 | ||
|
|
f392ad4053 | ||
|
|
35990f3bdd | ||
|
|
3932857c21 | ||
|
|
6f771a972b | ||
|
|
806a07135a | ||
|
|
f6b2287778 | ||
|
|
f4e90bb839 | ||
|
|
21630e919b | ||
|
|
ae74ab6906 | ||
|
|
caf361cd10 | ||
|
|
aeedc1946e | ||
|
|
93ae31f679 | ||
|
|
d5ff215da2 | ||
|
|
b34f7377da | ||
|
|
0c2e182039 | ||
|
|
438daf27ee | ||
|
|
aa6634970a | ||
|
|
9052416786 | ||
|
|
9d19eaf667 | ||
|
|
7ae9afa63a | ||
|
|
3d856b3773 | ||
|
|
7617b2382a | ||
|
|
a03dcf6859 | ||
|
|
57769ba199 | ||
|
|
6304b3a619 | ||
|
|
d1649f3c33 | ||
|
|
80f507b4cc | ||
|
|
f8a665db65 | ||
|
|
b15f2a8a26 | ||
|
|
e9094097a2 | ||
|
|
bea6ac5e68 | ||
|
|
778533ac7f | ||
|
|
83fcde5bd0 | ||
|
|
c9ec5874d3 | ||
|
|
4ce5fd25bc | ||
|
|
1ed28ebc9b | ||
|
|
c03199f7e5 | ||
|
|
9f10bcdec4 | ||
|
|
82c96ddfe3 | ||
|
|
c0fea076b9 | ||
|
|
b779ff5d1c | ||
|
|
73c10906e3 | ||
|
|
0ccd545dd4 | ||
|
|
49da0b47c7 | ||
|
|
672b28a5b7 | ||
|
|
2dbada3c7c | ||
|
|
3dbc105fbd | ||
|
|
43d55211b0 | ||
|
|
f170e9df69 | ||
|
|
1fbb6b0754 | ||
|
|
4c526d6f08 | ||
|
|
a62ae8c757 | ||
|
|
f2eb53569b | ||
|
|
c2a6a3d035 | ||
|
|
97dcc65eac | ||
|
|
1bd76dbb61 | ||
|
|
0fdf894bf0 | ||
|
|
19202e07d4 | ||
|
|
84b624aea2 | ||
|
|
c7ac5087b3 | ||
|
|
4ff8cd6d90 | ||
|
|
3f4558da2b | ||
|
|
b976e79b0f | ||
|
|
3d064fa68d | ||
|
|
1a1e024178 | ||
|
|
4876d62b56 | ||
|
|
8006b7096f | ||
|
|
a82ce7310d | ||
|
|
ae08cb62c5 | ||
|
|
c48fba6c01 | ||
|
|
3db25e7e3b | ||
|
|
1f7a49378b | ||
|
|
37ec21f5be | ||
|
|
2b8498f569 | ||
|
|
5c4fc61a12 | ||
|
|
c0ec65bbae |
4
.gitignore
vendored
@@ -28,14 +28,12 @@ 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
|
||||||
MasterPassword/C/mpw-*.tar.gz
|
MasterPassword/C/mpw-*.tar.gz
|
||||||
MasterPassword/C/mpw
|
MasterPassword/C/mpw
|
||||||
MasterPassword/C/mpw-bench
|
MasterPassword/C/mpw-bench
|
||||||
|
MasterPassword/C/mpw-tests
|
||||||
MasterPassword/C/lib/*/*
|
MasterPassword/C/lib/*/*
|
||||||
!MasterPassword/C/lib/*/.source
|
!MasterPassword/C/lib/*/.source
|
||||||
|
|||||||
2
.gitmodules
vendored
@@ -18,4 +18,4 @@
|
|||||||
url = git://github.com/jonmarimba/jrswizzle.git
|
url = git://github.com/jonmarimba/jrswizzle.git
|
||||||
[submodule "Site/mpw-js/js/mpw-js"]
|
[submodule "Site/mpw-js/js/mpw-js"]
|
||||||
path = Site/mpw-js/js/mpw-js
|
path = Site/mpw-js/js/mpw-js
|
||||||
url = https://github.com/Lyndir/mpw-js.git
|
url = https://github.com/tmthrgd/mpw-js.git
|
||||||
|
|||||||
2
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -2,6 +2,7 @@
|
|||||||
<profile version="1.0" is_locked="false">
|
<profile version="1.0" is_locked="false">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
<option name="myLocal" value="false" />
|
<option name="myLocal" value="false" />
|
||||||
|
<inspection_tool class="Convert to string" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="FunctionImplicitDeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="LossyEncoding" enabled="true" level="WARNING" enabled_by_default="true" />
|
<inspection_tool class="LossyEncoding" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
<inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="OCNotLocalizedStringInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="OCUnusedMacroInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="OCUnusedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="Replace with subshell" enabled="true" level="INFO" enabled_by_default="true" />
|
||||||
<inspection_tool class="SignednessMismatch" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="SignednessMismatch" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="UnavailableInDeploymentTarget" enabled="true" level="INFO" enabled_by_default="true" />
|
<inspection_tool class="UnavailableInDeploymentTarget" enabled="true" level="INFO" enabled_by_default="true" />
|
||||||
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
|||||||
2
External/Pearl
vendored
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
# ______________________________________________________________________
|
# ______________________________________________________________________
|
||||||
# | |
|
# | |
|
||||||
# | .:: TABLE OF CONTENTS ::. |
|
# | .: TABLE OF CONTENTS :. |
|
||||||
# |______________________________________________________________________|
|
# |______________________________________________________________________|
|
||||||
#
|
#
|
||||||
# chr decimal
|
# chr decimal
|
||||||
@@ -66,12 +66,6 @@
|
|||||||
# readwhile command [args]
|
# readwhile command [args]
|
||||||
# Outputs the characters typed by the user into the terminal's input buffer while running the given command.
|
# Outputs the characters typed by the user into the terminal's input buffer while running the given command.
|
||||||
#
|
#
|
||||||
# pushqueue element ...
|
|
||||||
# Pushes the given arguments as elements onto the queue.
|
|
||||||
#
|
|
||||||
# popqueue
|
|
||||||
# Pops one element off the queue.
|
|
||||||
#
|
|
||||||
# log [format] [arguments...]
|
# log [format] [arguments...]
|
||||||
# Log an event at a certain importance level.
|
# Log an event at a certain importance level.
|
||||||
# The event is expressed as a printf(1) format argument.
|
# The event is expressed as a printf(1) format argument.
|
||||||
@@ -132,7 +126,7 @@ _tocHash=71e13f42e1ea82c1c7019b27a3bc71f3
|
|||||||
|
|
||||||
# ______________________________________________________________________
|
# ______________________________________________________________________
|
||||||
# | |
|
# | |
|
||||||
# | .:: GLOBAL CONFIGURATION ::. |
|
# | .: GLOBAL CONFIGURATION :. |
|
||||||
# |______________________________________________________________________|
|
# |______________________________________________________________________|
|
||||||
|
|
||||||
# Unset all exported functions. Exported functions are evil.
|
# Unset all exported functions. Exported functions are evil.
|
||||||
@@ -177,7 +171,7 @@ genToc() {
|
|||||||
|
|
||||||
# ______________________________________________________________________
|
# ______________________________________________________________________
|
||||||
# | |
|
# | |
|
||||||
# | .:: GLOBAL DECLARATIONS ::. |
|
# | .: GLOBAL DECLARATIONS :. |
|
||||||
# |______________________________________________________________________|
|
# |______________________________________________________________________|
|
||||||
|
|
||||||
# Variables for convenience sequences.
|
# Variables for convenience sequences.
|
||||||
@@ -190,8 +184,8 @@ runner=( '> >' \
|
|||||||
|
|
||||||
# Variables for terminal requests.
|
# Variables for terminal requests.
|
||||||
[[ -t 2 && $TERM != dumb ]] && {
|
[[ -t 2 && $TERM != dumb ]] && {
|
||||||
COLUMNS=$( tput cols || tput co ) # Columns in a line
|
COLUMNS=$({ tput cols || tput co;} 2>&3) # Columns in a line
|
||||||
LINES=$( tput lines || tput li ) # Lines on screen
|
LINES=$({ tput lines || tput li;} 2>&3) # Lines on screen
|
||||||
alt=$( tput smcup || tput ti ) # Start alt display
|
alt=$( tput smcup || tput ti ) # Start alt display
|
||||||
ealt=$( tput rmcup || tput te ) # End alt display
|
ealt=$( tput rmcup || tput te ) # End alt display
|
||||||
hide=$( tput civis || tput vi ) # Hide cursor
|
hide=$( tput civis || tput vi ) # Hide cursor
|
||||||
@@ -230,7 +224,7 @@ runner=( '> >' \
|
|||||||
tput eA; tput as;
|
tput eA; tput as;
|
||||||
tput ac; tput ae; } ) # Drawing characters
|
tput ac; tput ae; } ) # Drawing characters
|
||||||
back=$'\b'
|
back=$'\b'
|
||||||
} 2>/dev/null ||:
|
} 3>&2 2>/dev/null ||:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -238,7 +232,7 @@ runner=( '> >' \
|
|||||||
|
|
||||||
# ______________________________________________________________________
|
# ______________________________________________________________________
|
||||||
# | |
|
# | |
|
||||||
# | .:: FUNCTION DECLARATIONS ::. |
|
# | .: FUNCTION DECLARATIONS :. |
|
||||||
# |______________________________________________________________________|
|
# |______________________________________________________________________|
|
||||||
|
|
||||||
|
|
||||||
@@ -465,23 +459,6 @@ readwhile() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# __________________________________________________________________________
|
|
||||||
# |__ popqueue ______________________________________________________________|
|
|
||||||
#
|
|
||||||
# popqueue
|
|
||||||
#
|
|
||||||
# Pops one element off the queue.
|
|
||||||
# If no elements are available on the queue, this command fails with exit code 1.
|
|
||||||
#
|
|
||||||
popqueue() {
|
|
||||||
local REPLY
|
|
||||||
[[ $_queue ]] && read -t0 <&"${_queue[0]}" || return
|
|
||||||
IFS= read -r -d '' <&"${_queue[0]}"
|
|
||||||
printf %s "$REPLY"
|
|
||||||
} # _____________________________________________________________________
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ______________________________________________________________________
|
# ______________________________________________________________________
|
||||||
# |__ Latest ____________________________________________________________|
|
# |__ Latest ____________________________________________________________|
|
||||||
#
|
#
|
||||||
@@ -1566,7 +1543,7 @@ stackTrace() {
|
|||||||
|
|
||||||
# ______________________________________________________________________
|
# ______________________________________________________________________
|
||||||
# | |
|
# | |
|
||||||
# | .:: ENTRY POINT ::. |
|
# | .: ENTRY POINT :. |
|
||||||
# |______________________________________________________________________|
|
# |______________________________________________________________________|
|
||||||
|
|
||||||
# Make sure this file is sourced and not executed.
|
# Make sure this file is sourced and not executed.
|
||||||
@@ -1586,6 +1563,6 @@ stackTrace() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:
|
:
|
||||||
: .:: END SOURCING ::.
|
: .: END SOURCING :.
|
||||||
: ______________________________________________________________________
|
: ______________________________________________________________________
|
||||||
:
|
:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
# try ./build -lrt instead.
|
# try ./build -lrt instead.
|
||||||
# - If you see 'x86.S:202: Error: junk at end of line, first unrecognized character is `,'',
|
# - If you see 'x86.S:202: Error: junk at end of line, first unrecognized character is `,'',
|
||||||
# try commenting the line in lib/bcrypt/x86.S.
|
# try commenting the line in lib/bcrypt/x86.S.
|
||||||
|
# - Take a look at the "Optional features" section. Some features have dependencies,
|
||||||
|
# either make sure you have them or disable those features.
|
||||||
#
|
#
|
||||||
# BUGS
|
# BUGS
|
||||||
# masterpassword@lyndir.com
|
# masterpassword@lyndir.com
|
||||||
@@ -31,10 +33,14 @@ else
|
|||||||
# Modify here or override using targets='mpw mpw-bench' ./build
|
# Modify here or override using targets='mpw mpw-bench' ./build
|
||||||
targets=(
|
targets=(
|
||||||
mpw # C CLI version of Master Password.
|
mpw # C CLI version of Master Password.
|
||||||
#mpw-bench # C CLI Master Password benchmark utility.
|
mpw-bench # C CLI Master Password benchmark utility.
|
||||||
|
mpw-tests # C Master Password algorithm tester.
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Optional features.
|
||||||
|
mpw_color=0 # Colorized Identicon, requires libncurses-dev
|
||||||
|
|
||||||
|
|
||||||
### DEPENDENCIES
|
### DEPENDENCIES
|
||||||
|
|
||||||
@@ -75,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
|
||||||
@@ -128,12 +138,21 @@ fetchSource() (
|
|||||||
echo >&2 "error: into: $PWD"
|
echo >&2 "error: into: $PWD"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e .patched ]] && (( ${#patches[@]} )); then
|
||||||
|
for patch in "${patches[@]}"; do
|
||||||
|
echo
|
||||||
|
echo "Patching: ${PWD##*/}, for $patch..."
|
||||||
|
patch -p0 < ../"${PWD##*/}-$patch.patch"
|
||||||
|
done
|
||||||
|
touch .patched
|
||||||
|
fi
|
||||||
)
|
)
|
||||||
depend() {
|
depend() {
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Checking dependency: $1..."
|
echo "Checking dependency: $1..."
|
||||||
[[ -e "lib/$1/.built" ]] && return
|
[[ -e "lib/include/$1" ]] && return
|
||||||
|
|
||||||
pushd "lib/$1"
|
pushd "lib/$1"
|
||||||
fetchSource
|
fetchSource
|
||||||
@@ -169,7 +188,8 @@ depend() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
make
|
make
|
||||||
date > .built
|
install -d "../include/$1/"
|
||||||
|
find . -name '*.h' -exec install -m 444 {} "../include/$1/" \;
|
||||||
else
|
else
|
||||||
echo >&2 "error: Don't know how to build: $1"
|
echo >&2 "error: Don't know how to build: $1"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -186,13 +206,9 @@ mpw() {
|
|||||||
echo "Building target: $target..."
|
echo "Building target: $target..."
|
||||||
CFLAGS=(
|
CFLAGS=(
|
||||||
# include paths
|
# include paths
|
||||||
-I"lib/scrypt/lib" -I"lib/scrypt/libcperciva"
|
-I"lib/include"
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
LDFLAGS=(
|
||||||
# library paths
|
|
||||||
-L"." -L"lib/scrypt"
|
|
||||||
# link libraries
|
|
||||||
-l"crypto" -l"curses"
|
|
||||||
# scrypt
|
# scrypt
|
||||||
"lib/scrypt/scrypt-crypto_aesctr.o"
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
"lib/scrypt/scrypt-sha256.o"
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
@@ -200,10 +216,19 @@ mpw() {
|
|||||||
"lib/scrypt/scrypt-memlimit.o"
|
"lib/scrypt/scrypt-memlimit.o"
|
||||||
"lib/scrypt/scrypt-scryptenc_cpuperf.o"
|
"lib/scrypt/scrypt-scryptenc_cpuperf.o"
|
||||||
"lib/scrypt/scrypt-scryptenc.o"
|
"lib/scrypt/scrypt-scryptenc.o"
|
||||||
|
# library paths
|
||||||
|
-L"." -L"lib/scrypt"
|
||||||
|
# link libraries
|
||||||
|
-l"crypto"
|
||||||
)
|
)
|
||||||
|
# optional features
|
||||||
|
(( mpw_color )) && CFLAGS+=( -DCOLOR ) LDFLAGS+=( -l"curses" )
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" -c types.c -o types.o "$@"
|
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
|
||||||
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "types.o" mpw.c -o mpw "$@"
|
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-cli.c" -o "mpw"
|
||||||
echo "done! Now run ./install or use ./mpw"
|
echo "done! Now run ./install or use ./mpw"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,15 +242,9 @@ mpw-bench() {
|
|||||||
echo "Building target: $target..."
|
echo "Building target: $target..."
|
||||||
CFLAGS=(
|
CFLAGS=(
|
||||||
# include paths
|
# include paths
|
||||||
-I"lib/scrypt/lib" -I"lib/scrypt/libcperciva"
|
-I"lib/include"
|
||||||
-I"lib/bcrypt"
|
|
||||||
)
|
)
|
||||||
LDFLAGS=(
|
LDFLAGS=(
|
||||||
# library paths
|
|
||||||
-L"." -L"lib/scrypt"
|
|
||||||
-L"lib/bcrypt"
|
|
||||||
# libraries
|
|
||||||
-l"crypto"
|
|
||||||
# scrypt
|
# scrypt
|
||||||
"lib/scrypt/scrypt-crypto_aesctr.o"
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
"lib/scrypt/scrypt-sha256.o"
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
@@ -238,14 +257,52 @@ mpw-bench() {
|
|||||||
"lib/bcrypt/crypt_gensalt.o"
|
"lib/bcrypt/crypt_gensalt.o"
|
||||||
"lib/bcrypt/wrapper.o"
|
"lib/bcrypt/wrapper.o"
|
||||||
"lib/bcrypt/x86.o"
|
"lib/bcrypt/x86.o"
|
||||||
|
# library paths
|
||||||
|
-L"." -L"lib/scrypt"
|
||||||
|
-L"lib/bcrypt"
|
||||||
|
# link libraries
|
||||||
|
-l"crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
cc "${CFLAGS[@]}" -c types.c -o types.o "$@"
|
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
|
||||||
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "types.o" mpw-bench.c -o mpw-bench "$@"
|
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
|
||||||
echo "done! Now use ./mpw-bench"
|
echo "done! Now use ./mpw-bench"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
### MPW-TESTS
|
||||||
|
mpw-tests() {
|
||||||
|
depend scrypt
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Building target: $target..."
|
||||||
|
CFLAGS=(
|
||||||
|
# include paths
|
||||||
|
-I"lib/include"
|
||||||
|
-I"/usr/include/libxml2"
|
||||||
|
-I"/usr/local/include/libxml2"
|
||||||
|
)
|
||||||
|
LDFLAGS=(
|
||||||
|
# scrypt
|
||||||
|
"lib/scrypt/scrypt-crypto_aesctr.o"
|
||||||
|
"lib/scrypt/scrypt-sha256.o"
|
||||||
|
"lib/scrypt/scrypt-crypto_scrypt-nosse.o"
|
||||||
|
"lib/scrypt/scrypt-memlimit.o"
|
||||||
|
"lib/scrypt/scrypt-scryptenc_cpuperf.o"
|
||||||
|
"lib/scrypt/scrypt-scryptenc.o"
|
||||||
|
# library paths
|
||||||
|
-L"." -L"lib/scrypt"
|
||||||
|
# link libraries
|
||||||
|
-l"crypto" -l"xml2"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
### TARGETS
|
### TARGETS
|
||||||
|
|
||||||
haslib() {
|
haslib() {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ fi
|
|||||||
echo
|
echo
|
||||||
|
|
||||||
inf "You can also save your user name in ~/.bashrc. Leave blank to skip this step."
|
inf "You can also save your user name in ~/.bashrc. Leave blank to skip this step."
|
||||||
if MP_USERNAME=$(ask "Your full name:") && [[ $MP_USERNAME ]] ; then
|
if MP_FULLNAME=$(ask "Your full name:") && [[ $MP_FULLNAME ]] ; then
|
||||||
printf 'export MP_USERNAME=%q\n' "$MP_USERNAME" >> ~/.bashrc
|
printf 'export MP_FULLNAME=%q\n' "$MP_FULLNAME" >> ~/.bashrc
|
||||||
fi
|
fi
|
||||||
echo
|
echo
|
||||||
|
|
||||||
|
|||||||
12
MasterPassword/C/lib/bcrypt-arm.patch
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
--- x86.S 2014-11-21 09:09:58.000000000 -0500
|
||||||
|
+++ x86.S 2014-11-21 09:11:01.000000000 -0500
|
||||||
|
@@ -199,5 +199,9 @@
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__ELF__) && defined(__linux__)
|
||||||
|
+#if defined(__arm__)
|
||||||
|
+.section .note.GNU-stack,"",%progbits
|
||||||
|
+#else
|
||||||
|
.section .note.GNU-stack,"",@progbits
|
||||||
|
#endif
|
||||||
|
+#endif
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
home=http://www.openwall.com/crypt/
|
home=http://www.openwall.com/crypt/
|
||||||
pkg=http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz
|
pkg=http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz
|
||||||
pkg_sha256=83fa01fca6996fe8d882b7f8e9ba0305a5664936100b01481ea3c6a8ce8d72fd
|
pkg_sha256=83fa01fca6996fe8d882b7f8e9ba0305a5664936100b01481ea3c6a8ce8d72fd
|
||||||
|
patches=(arm)
|
||||||
|
|||||||
53
MasterPassword/C/mpw-algorithm.c
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// mpw-algorithm.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "mpw-algorithm.h"
|
||||||
|
#include "mpw-algorithm_v0.c"
|
||||||
|
#include "mpw-algorithm_v1.c"
|
||||||
|
#include "mpw-algorithm_v2.c"
|
||||||
|
#include "mpw-algorithm_v3.c"
|
||||||
|
|
||||||
|
#define MP_N 32768
|
||||||
|
#define MP_r 8
|
||||||
|
#define MP_p 2
|
||||||
|
#define MP_hash PearlHashSHA256
|
||||||
|
|
||||||
|
const uint8_t *mpw_masterKeyForUser(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
|
||||||
|
|
||||||
|
switch (algorithmVersion) {
|
||||||
|
case MPAlgorithmVersion0:
|
||||||
|
return mpw_masterKeyForUser_v0( fullName, masterPassword );
|
||||||
|
case MPAlgorithmVersion1:
|
||||||
|
return mpw_masterKeyForUser_v1( fullName, masterPassword );
|
||||||
|
case MPAlgorithmVersion2:
|
||||||
|
return mpw_masterKeyForUser_v2( fullName, masterPassword );
|
||||||
|
case MPAlgorithmVersion3:
|
||||||
|
return mpw_masterKeyForUser_v3( fullName, masterPassword );
|
||||||
|
default:
|
||||||
|
ftl( "Unsupported version: %d", algorithmVersion );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 MPAlgorithmVersion algorithmVersion) {
|
||||||
|
|
||||||
|
switch (algorithmVersion) {
|
||||||
|
case MPAlgorithmVersion0:
|
||||||
|
return mpw_passwordForSite_v0( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||||
|
case MPAlgorithmVersion1:
|
||||||
|
return mpw_passwordForSite_v1( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||||
|
case MPAlgorithmVersion2:
|
||||||
|
return mpw_passwordForSite_v2( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||||
|
case MPAlgorithmVersion3:
|
||||||
|
return mpw_passwordForSite_v3( masterKey, siteName, siteType, siteCounter, siteVariant, siteContext );
|
||||||
|
default:
|
||||||
|
ftl( "Unsupported version: %d", algorithmVersion );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
MasterPassword/C/mpw-algorithm.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// mpw-algorithm.h
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "mpw-types.h"
|
||||||
|
|
||||||
|
typedef enum(unsigned int, MPAlgorithmVersion) {
|
||||||
|
/** V0 did math with chars whose signedness was platform-dependent. */
|
||||||
|
MPAlgorithmVersion0,
|
||||||
|
/** V1 miscounted the byte-length of multi-byte site names. */
|
||||||
|
MPAlgorithmVersion1,
|
||||||
|
/** V2 miscounted the byte-length of multi-byte user names. */
|
||||||
|
MPAlgorithmVersion2,
|
||||||
|
/** V3 is the current version. */
|
||||||
|
MPAlgorithmVersion3,
|
||||||
|
};
|
||||||
|
#define MPAlgorithmVersionCurrent MPAlgorithmVersion3
|
||||||
|
|
||||||
|
/** Derive the master key for a user based on their name and master password.
|
||||||
|
* @return A new MP_dkLen-byte allocated buffer or NULL if an allocation error occurred. */
|
||||||
|
const uint8_t *mpw_masterKeyForUser(
|
||||||
|
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
|
||||||
|
|
||||||
|
/** Encode a password for the site from the given master key and site parameters.
|
||||||
|
* @return A newly allocated string or NULL if an allocation error occurred. */
|
||||||
|
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 MPAlgorithmVersion algorithmVersion);
|
||||||
125
MasterPassword/C/mpw-algorithm_v0.c
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
//
|
||||||
|
// mpw-algorithm.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
#define MP_N 32768
|
||||||
|
#define MP_r 8
|
||||||
|
#define MP_p 2
|
||||||
|
#define MP_hash PearlHashSHA256
|
||||||
|
|
||||||
|
static const char *mpw_templateForType_v0(MPSiteType type, uint16_t seedByte) {
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
const char **templates = mpw_templatesForType( type, &count );
|
||||||
|
if (!count)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return templates[seedByte % count];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char mpw_characterFromClass_v0(char characterClass, uint16_t seedByte) {
|
||||||
|
|
||||||
|
const char *classCharacters = mpw_charactersInClass( characterClass );
|
||||||
|
return classCharacters[seedByte % strlen( classCharacters )];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const uint8_t *mpw_masterKeyForUser_v0(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
|
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||||
|
trc( "fullName: %s\n", fullName );
|
||||||
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
|
// Calculate the master key salt.
|
||||||
|
// masterKeySalt = mpKeyScope . #fullName . fullName
|
||||||
|
size_t masterKeySaltSize = 0;
|
||||||
|
uint8_t *masterKeySalt = NULL;
|
||||||
|
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
||||||
|
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
|
||||||
|
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
|
||||||
|
if (!masterKeySalt) {
|
||||||
|
ftl( "Could not allocate master key salt: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
|
||||||
|
|
||||||
|
// Calculate the master key.
|
||||||
|
// masterKey = scrypt( masterPassword, masterKeySalt )
|
||||||
|
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
||||||
|
mpw_free( masterKeySalt, masterKeySaltSize );
|
||||||
|
if (!masterKey) {
|
||||||
|
ftl( "Could not allocate master key: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
|
||||||
|
|
||||||
|
return masterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *mpw_passwordForSite_v0(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||||
|
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||||
|
|
||||||
|
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||||
|
trc( "siteName: %s\n", siteName );
|
||||||
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
|
trc( "siteType: %d\n", siteType );
|
||||||
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
||||||
|
|
||||||
|
// Calculate the site seed.
|
||||||
|
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||||
|
size_t sitePasswordInfoSize = 0;
|
||||||
|
uint8_t *sitePasswordInfo = NULL;
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteName ) ) );
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
||||||
|
if (siteContext) {
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteContext ) ) );
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
||||||
|
}
|
||||||
|
if (!sitePasswordInfo) {
|
||||||
|
ftl( "Could not allocate site seed info: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
|
||||||
|
|
||||||
|
const char *sitePasswordSeed = (const char *)mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
||||||
|
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
||||||
|
if (!sitePasswordSeed) {
|
||||||
|
ftl( "Could not allocate site seed: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
|
||||||
|
|
||||||
|
// Determine the template.
|
||||||
|
const char *template = mpw_templateForType_v0( siteType, htons( sitePasswordSeed[0] ) );
|
||||||
|
trc( "type %d, template: %s\n", siteType, template );
|
||||||
|
if (strlen( template ) > 32) {
|
||||||
|
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
||||||
|
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the password from the seed using the template.
|
||||||
|
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
||||||
|
for (size_t c = 0; c < strlen( template ); ++c) {
|
||||||
|
sitePassword[c] = mpw_characterFromClass_v0( template[c], htons( sitePasswordSeed[c + 1] ) );
|
||||||
|
trc( "class %c, index %u (0x%02X) -> character: %c\n",
|
||||||
|
template[c], htons( sitePasswordSeed[c + 1] ), htons( sitePasswordSeed[c + 1] ), sitePassword[c] );
|
||||||
|
}
|
||||||
|
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||||
|
|
||||||
|
return sitePassword;
|
||||||
|
}
|
||||||
109
MasterPassword/C/mpw-algorithm_v1.c
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//
|
||||||
|
// mpw-algorithm.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
#define MP_N 32768
|
||||||
|
#define MP_r 8
|
||||||
|
#define MP_p 2
|
||||||
|
#define MP_hash PearlHashSHA256
|
||||||
|
|
||||||
|
static const uint8_t *mpw_masterKeyForUser_v1(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
|
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||||
|
trc( "fullName: %s\n", fullName );
|
||||||
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
|
// Calculate the master key salt.
|
||||||
|
// masterKeySalt = mpKeyScope . #fullName . fullName
|
||||||
|
size_t masterKeySaltSize = 0;
|
||||||
|
uint8_t *masterKeySalt = NULL;
|
||||||
|
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
||||||
|
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
|
||||||
|
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
|
||||||
|
if (!masterKeySalt) {
|
||||||
|
ftl( "Could not allocate master key salt: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
|
||||||
|
|
||||||
|
// Calculate the master key.
|
||||||
|
// masterKey = scrypt( masterPassword, masterKeySalt )
|
||||||
|
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
||||||
|
mpw_free( masterKeySalt, masterKeySaltSize );
|
||||||
|
if (!masterKey) {
|
||||||
|
ftl( "Could not allocate master key: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
|
||||||
|
|
||||||
|
return masterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *mpw_passwordForSite_v1(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||||
|
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||||
|
|
||||||
|
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||||
|
trc( "siteName: %s\n", siteName );
|
||||||
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
|
trc( "siteType: %d\n", siteType );
|
||||||
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
||||||
|
|
||||||
|
// Calculate the site seed.
|
||||||
|
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||||
|
size_t sitePasswordInfoSize = 0;
|
||||||
|
uint8_t *sitePasswordInfo = NULL;
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteName ) ) );
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
||||||
|
if (siteContext) {
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( mpw_charlen( siteContext ) ) );
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
||||||
|
}
|
||||||
|
if (!sitePasswordInfo) {
|
||||||
|
ftl( "Could not allocate site seed info: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
|
||||||
|
|
||||||
|
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
||||||
|
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
||||||
|
if (!sitePasswordSeed) {
|
||||||
|
ftl( "Could not allocate site seed: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
|
||||||
|
|
||||||
|
// Determine the template.
|
||||||
|
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
||||||
|
trc( "type %d, template: %s\n", siteType, template );
|
||||||
|
if (strlen( template ) > 32) {
|
||||||
|
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
||||||
|
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the password from the seed using the template.
|
||||||
|
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
||||||
|
for (size_t c = 0; c < strlen( template ); ++c) {
|
||||||
|
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
||||||
|
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
||||||
|
sitePassword[c] );
|
||||||
|
}
|
||||||
|
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||||
|
|
||||||
|
return sitePassword;
|
||||||
|
}
|
||||||
109
MasterPassword/C/mpw-algorithm_v2.c
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//
|
||||||
|
// mpw-algorithm.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
#define MP_N 32768
|
||||||
|
#define MP_r 8
|
||||||
|
#define MP_p 2
|
||||||
|
#define MP_hash PearlHashSHA256
|
||||||
|
|
||||||
|
static const uint8_t *mpw_masterKeyForUser_v2(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
|
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||||
|
trc( "fullName: %s\n", fullName );
|
||||||
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
|
// Calculate the master key salt.
|
||||||
|
// masterKeySalt = mpKeyScope . #fullName . fullName
|
||||||
|
size_t masterKeySaltSize = 0;
|
||||||
|
uint8_t *masterKeySalt = NULL;
|
||||||
|
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
||||||
|
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( mpw_charlen( fullName ) ) );
|
||||||
|
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
|
||||||
|
if (!masterKeySalt) {
|
||||||
|
ftl( "Could not allocate master key salt: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
|
||||||
|
|
||||||
|
// Calculate the master key.
|
||||||
|
// masterKey = scrypt( masterPassword, masterKeySalt )
|
||||||
|
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
||||||
|
mpw_free( masterKeySalt, masterKeySaltSize );
|
||||||
|
if (!masterKey) {
|
||||||
|
ftl( "Could not allocate master key: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
|
||||||
|
|
||||||
|
return masterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *mpw_passwordForSite_v2(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||||
|
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||||
|
|
||||||
|
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||||
|
trc( "siteName: %s\n", siteName );
|
||||||
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
|
trc( "siteType: %d\n", siteType );
|
||||||
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
||||||
|
|
||||||
|
// Calculate the site seed.
|
||||||
|
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||||
|
size_t sitePasswordInfoSize = 0;
|
||||||
|
uint8_t *sitePasswordInfo = NULL;
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
||||||
|
if (siteContext) {
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
||||||
|
}
|
||||||
|
if (!sitePasswordInfo) {
|
||||||
|
ftl( "Could not allocate site seed info: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
|
||||||
|
|
||||||
|
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
||||||
|
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
||||||
|
if (!sitePasswordSeed) {
|
||||||
|
ftl( "Could not allocate site seed: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
|
||||||
|
|
||||||
|
// Determine the template.
|
||||||
|
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
||||||
|
trc( "type %d, template: %s\n", siteType, template );
|
||||||
|
if (strlen( template ) > 32) {
|
||||||
|
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
||||||
|
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the password from the seed using the template.
|
||||||
|
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
||||||
|
for (size_t c = 0; c < strlen( template ); ++c) {
|
||||||
|
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
||||||
|
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
||||||
|
sitePassword[c] );
|
||||||
|
}
|
||||||
|
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||||
|
|
||||||
|
return sitePassword;
|
||||||
|
}
|
||||||
109
MasterPassword/C/mpw-algorithm_v3.c
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//
|
||||||
|
// mpw-algorithm.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
#define MP_N 32768
|
||||||
|
#define MP_r 8
|
||||||
|
#define MP_p 2
|
||||||
|
#define MP_hash PearlHashSHA256
|
||||||
|
|
||||||
|
static const uint8_t *mpw_masterKeyForUser_v3(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
|
const char *mpKeyScope = mpw_scopeForVariant( MPSiteVariantPassword );
|
||||||
|
trc( "fullName: %s\n", fullName );
|
||||||
|
trc( "masterPassword: %s\n", masterPassword );
|
||||||
|
trc( "key scope: %s\n", mpKeyScope );
|
||||||
|
|
||||||
|
// Calculate the master key salt.
|
||||||
|
// masterKeySalt = mpKeyScope . #fullName . fullName
|
||||||
|
size_t masterKeySaltSize = 0;
|
||||||
|
uint8_t *masterKeySalt = NULL;
|
||||||
|
mpw_pushString( &masterKeySalt, &masterKeySaltSize, mpKeyScope );
|
||||||
|
mpw_pushInt( &masterKeySalt, &masterKeySaltSize, htonl( strlen( fullName ) ) );
|
||||||
|
mpw_pushString( &masterKeySalt, &masterKeySaltSize, fullName );
|
||||||
|
if (!masterKeySalt) {
|
||||||
|
ftl( "Could not allocate master key salt: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "masterKeySalt ID: %s\n", mpw_idForBuf( masterKeySalt, masterKeySaltSize ) );
|
||||||
|
|
||||||
|
// Calculate the master key.
|
||||||
|
// masterKey = scrypt( masterPassword, masterKeySalt )
|
||||||
|
const uint8_t *masterKey = mpw_scrypt( MP_dkLen, masterPassword, masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
|
||||||
|
mpw_free( masterKeySalt, masterKeySaltSize );
|
||||||
|
if (!masterKey) {
|
||||||
|
ftl( "Could not allocate master key: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "masterKey ID: %s\n", mpw_idForBuf( masterKey, MP_dkLen ) );
|
||||||
|
|
||||||
|
return masterKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *mpw_passwordForSite_v3(const uint8_t *masterKey, const char *siteName, const MPSiteType siteType, const uint32_t siteCounter,
|
||||||
|
const MPSiteVariant siteVariant, const char *siteContext) {
|
||||||
|
|
||||||
|
const char *siteScope = mpw_scopeForVariant( siteVariant );
|
||||||
|
trc( "siteName: %s\n", siteName );
|
||||||
|
trc( "siteCounter: %d\n", siteCounter );
|
||||||
|
trc( "siteVariant: %d\n", siteVariant );
|
||||||
|
trc( "siteType: %d\n", siteType );
|
||||||
|
trc( "site scope: %s, context: %s\n", siteScope, siteContext == NULL? "<empty>": siteContext );
|
||||||
|
|
||||||
|
// Calculate the site seed.
|
||||||
|
// sitePasswordSeed = hmac-sha256( masterKey, siteScope . #siteName . siteName . siteCounter . #siteContext . siteContext )
|
||||||
|
size_t sitePasswordInfoSize = 0;
|
||||||
|
uint8_t *sitePasswordInfo = NULL;
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteScope );
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteName ) ) );
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteName );
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( siteCounter ) );
|
||||||
|
if (siteContext) {
|
||||||
|
mpw_pushInt( &sitePasswordInfo, &sitePasswordInfoSize, htonl( strlen( siteContext ) ) );
|
||||||
|
mpw_pushString( &sitePasswordInfo, &sitePasswordInfoSize, siteContext );
|
||||||
|
}
|
||||||
|
if (!sitePasswordInfo) {
|
||||||
|
ftl( "Could not allocate site seed info: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "sitePasswordInfo ID: %s\n", mpw_idForBuf( sitePasswordInfo, sitePasswordInfoSize ) );
|
||||||
|
|
||||||
|
const uint8_t *sitePasswordSeed = mpw_hmac_sha256( masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoSize );
|
||||||
|
mpw_free( sitePasswordInfo, sitePasswordInfoSize );
|
||||||
|
if (!sitePasswordSeed) {
|
||||||
|
ftl( "Could not allocate site seed: %d\n", errno );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
trc( "sitePasswordSeed ID: %s\n", mpw_idForBuf( sitePasswordSeed, 32 ) );
|
||||||
|
|
||||||
|
// Determine the template.
|
||||||
|
const char *template = mpw_templateForType( siteType, sitePasswordSeed[0] );
|
||||||
|
trc( "type %d, template: %s\n", siteType, template );
|
||||||
|
if (strlen( template ) > 32) {
|
||||||
|
ftl( "Template too long for password seed: %lu", strlen( template ) );
|
||||||
|
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the password from the seed using the template.
|
||||||
|
char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
|
||||||
|
for (size_t c = 0; c < strlen( template ); ++c) {
|
||||||
|
sitePassword[c] = mpw_characterFromClass( template[c], sitePasswordSeed[c + 1] );
|
||||||
|
trc( "class %c, index %u (0x%02X) -> character: %c\n", template[c], sitePasswordSeed[c + 1], sitePasswordSeed[c + 1],
|
||||||
|
sitePassword[c] );
|
||||||
|
}
|
||||||
|
mpw_free( sitePasswordSeed, sizeof( sitePasswordSeed ) );
|
||||||
|
|
||||||
|
return sitePassword;
|
||||||
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
#include <sys/time.h>
|
//
|
||||||
|
// mpw-bench.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include <alg/sha256.h>
|
#include <scrypt/sha256.h>
|
||||||
#include <crypto/crypto_scrypt.h>
|
#include <bcrypt/ow-crypt.h>
|
||||||
#include <ow-crypt.h>
|
|
||||||
#include "types.h"
|
#include "mpw-algorithm.h"
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
#define MP_N 32768
|
#define MP_N 32768
|
||||||
#define MP_r 8
|
#define MP_r 8
|
||||||
@@ -22,166 +24,84 @@
|
|||||||
#define MP_dkLen 64
|
#define MP_dkLen 64
|
||||||
#define MP_hash PearlHashSHA256
|
#define MP_hash PearlHashSHA256
|
||||||
|
|
||||||
|
static void mpw_getTime(struct timeval *time) {
|
||||||
|
|
||||||
|
if (gettimeofday( time, NULL ) != 0)
|
||||||
|
ftl( "Could not get time: %d\n", errno );
|
||||||
|
}
|
||||||
|
|
||||||
|
static const double mpw_showSpeed(struct timeval startTime, const unsigned int iterations, const char *operation) {
|
||||||
|
|
||||||
|
struct timeval endTime;
|
||||||
|
mpw_getTime( &endTime );
|
||||||
|
|
||||||
|
const time_t dsec = (endTime.tv_sec - startTime.tv_sec);
|
||||||
|
const suseconds_t dusec = (endTime.tv_usec - startTime.tv_usec);
|
||||||
|
const double elapsed = dsec + dusec / 1000000.;
|
||||||
|
const double speed = iterations / elapsed;
|
||||||
|
|
||||||
|
fprintf( stderr, " done. " );
|
||||||
|
fprintf( stdout, "%d %s iterations in %llds %lldµs -> %.2f/s\n", iterations, operation, (long long)dsec, (long long)dusec, speed );
|
||||||
|
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *const argv[]) {
|
int main(int argc, char *const argv[]) {
|
||||||
|
|
||||||
char *userName = "Robert Lee Mitchel";
|
const char *fullName = "Robert Lee Mitchel";
|
||||||
char *masterPassword = "banana colored duckling";
|
const char *masterPassword = "banana colored duckling";
|
||||||
char *siteName = "masterpasswordapp.com";
|
const char *siteName = "masterpasswordapp.com";
|
||||||
uint32_t siteCounter = 1;
|
const uint32_t siteCounter = 1;
|
||||||
MPElementType siteType = MPElementTypeGeneratedLong;
|
const MPSiteType siteType = MPSiteTypeGeneratedLong;
|
||||||
|
const MPSiteVariant siteVariant = MPSiteVariantPassword;
|
||||||
// Start MP
|
const char *siteContext = NULL;
|
||||||
struct timeval startTime;
|
struct timeval startTime;
|
||||||
if (gettimeofday(&startTime, NULL) != 0) {
|
|
||||||
fprintf(stderr, "Could not get time: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int iterations = 100;
|
// Start MPW
|
||||||
|
unsigned int iterations = 100;
|
||||||
|
mpw_getTime( &startTime );
|
||||||
for (int i = 0; i < iterations; ++i) {
|
for (int i = 0; i < iterations; ++i) {
|
||||||
// Calculate the master key salt.
|
const uint8_t *masterKey = mpw_masterKeyForUser(
|
||||||
char *mpNameSpace = "com.lyndir.masterpassword";
|
fullName, masterPassword, MPAlgorithmVersionCurrent );
|
||||||
const uint32_t n_userNameLength = htonl(strlen(userName));
|
if (!masterKey)
|
||||||
const size_t masterKeySaltLength = strlen(mpNameSpace) + sizeof(n_userNameLength) + strlen(userName);
|
ftl( "Could not allocate master key: %d\n", errno );
|
||||||
char *masterKeySalt = malloc( masterKeySaltLength );
|
free( (void *)mpw_passwordForSite(
|
||||||
if (!masterKeySalt) {
|
masterKey, siteName, siteType, siteCounter, siteVariant, siteContext, MPAlgorithmVersionCurrent ) );
|
||||||
fprintf(stderr, "Could not allocate master key salt: %d\n", errno);
|
free( (void *)masterKey );
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *mKS = masterKeySalt;
|
|
||||||
memcpy(mKS, mpNameSpace, strlen(mpNameSpace)); mKS += strlen(mpNameSpace);
|
|
||||||
memcpy(mKS, &n_userNameLength, sizeof(n_userNameLength)); mKS += sizeof(n_userNameLength);
|
|
||||||
memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName);
|
|
||||||
if (mKS - masterKeySalt != masterKeySaltLength)
|
|
||||||
abort();
|
|
||||||
trc("masterKeySalt ID: %s\n", IDForBuf(masterKeySalt, masterKeySaltLength));
|
|
||||||
|
|
||||||
// Calculate the master key.
|
|
||||||
uint8_t *masterKey = malloc( MP_dkLen );
|
|
||||||
if (!masterKey) {
|
|
||||||
fprintf(stderr, "Could not allocate master key: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (crypto_scrypt( (const uint8_t *)masterPassword, strlen(masterPassword), (const uint8_t *)masterKeySalt, masterKeySaltLength, MP_N, MP_r, MP_p, masterKey, MP_dkLen ) < 0) {
|
|
||||||
fprintf(stderr, "Could not generate master key: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
memset(masterKeySalt, 0, masterKeySaltLength);
|
|
||||||
free(masterKeySalt);
|
|
||||||
|
|
||||||
// Calculate the site seed.
|
|
||||||
const uint32_t n_siteNameLength = htonl(strlen(siteName));
|
|
||||||
const uint32_t n_siteCounter = htonl(siteCounter);
|
|
||||||
const size_t sitePasswordInfoLength = strlen(mpNameSpace) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter);
|
|
||||||
char *sitePasswordInfo = malloc( sitePasswordInfoLength );
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
fprintf(stderr, "Could not allocate site seed: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *sPI = sitePasswordInfo;
|
|
||||||
memcpy(sPI, mpNameSpace, strlen(mpNameSpace)); sPI += strlen(mpNameSpace);
|
|
||||||
memcpy(sPI, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength);
|
|
||||||
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
|
|
||||||
memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter);
|
|
||||||
if (sPI - sitePasswordInfo != sitePasswordInfoLength)
|
|
||||||
abort();
|
|
||||||
|
|
||||||
uint8_t sitePasswordSeed[32];
|
|
||||||
HMAC_SHA256_Buf(masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoLength, sitePasswordSeed);
|
|
||||||
memset(masterKey, 0, MP_dkLen);
|
|
||||||
memset(sitePasswordInfo, 0, sitePasswordInfoLength);
|
|
||||||
free(masterKey);
|
|
||||||
free(sitePasswordInfo);
|
|
||||||
|
|
||||||
// Determine the cipher.
|
|
||||||
const char *cipher = CipherForType(siteType, sitePasswordSeed[0]);
|
|
||||||
trc("type %d, cipher: %s\n", siteType, cipher);
|
|
||||||
if (strlen(cipher) > 32)
|
|
||||||
abort();
|
|
||||||
|
|
||||||
// Encode the password from the seed using the cipher.
|
|
||||||
char *sitePassword = calloc(strlen(cipher) + 1, sizeof(char));
|
|
||||||
for (int c = 0; c < strlen(cipher); ++c) {
|
|
||||||
sitePassword[c] = CharacterFromClass(cipher[c], sitePasswordSeed[c + 1]);
|
|
||||||
trc("class %c, character: %c\n", cipher[c], sitePassword[c]);
|
|
||||||
}
|
|
||||||
memset(sitePasswordSeed, 0, sizeof(sitePasswordSeed));
|
|
||||||
|
|
||||||
if (i % 1 == 0)
|
if (i % 1 == 0)
|
||||||
fprintf( stderr, "\rmpw: iteration %d / %d..", i, iterations );
|
fprintf( stderr, "\rmpw: iteration %d / %d..", i, iterations );
|
||||||
}
|
}
|
||||||
|
const double mpwSpeed = mpw_showSpeed( startTime, iterations, "mpw" );
|
||||||
// Output timing results.
|
|
||||||
struct timeval endTime;
|
|
||||||
if (gettimeofday(&endTime, NULL) != 0) {
|
|
||||||
fprintf(stderr, "Could not get time: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
long long secs = (endTime.tv_sec - startTime.tv_sec);
|
|
||||||
long long usecs = (endTime.tv_usec - startTime.tv_usec);
|
|
||||||
double elapsed = secs + usecs / 1000000.0;
|
|
||||||
double mpwSpeed = iterations / elapsed;
|
|
||||||
fprintf( stdout, " done. %d iterations in %llds %lldµs -> %.2f/s\n", iterations, secs, usecs, mpwSpeed );
|
|
||||||
|
|
||||||
// Start SHA-256
|
// Start SHA-256
|
||||||
if (gettimeofday(&startTime, NULL) != 0) {
|
|
||||||
fprintf(stderr, "Could not get time: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterations = 50000000;
|
iterations = 50000000;
|
||||||
uint8_t hash[32];
|
uint8_t hash[32];
|
||||||
|
mpw_getTime( &startTime );
|
||||||
for (int i = 0; i < iterations; ++i) {
|
for (int i = 0; i < iterations; ++i) {
|
||||||
SHA256_Buf(masterPassword, strlen(masterPassword), hash);
|
SHA256_Buf( masterPassword, strlen( masterPassword ), hash );
|
||||||
|
|
||||||
if (i % 1000 == 0)
|
if (i % 1000 == 0)
|
||||||
fprintf( stderr, "\rsha256: iteration %d / %d..", i, iterations );
|
fprintf( stderr, "\rsha256: iteration %d / %d..", i, iterations );
|
||||||
}
|
}
|
||||||
|
const double sha256Speed = mpw_showSpeed( startTime, iterations, "sha256" );
|
||||||
// Output timing results.
|
|
||||||
if (gettimeofday(&endTime, NULL) != 0) {
|
|
||||||
fprintf(stderr, "Could not get time: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
secs = (endTime.tv_sec - startTime.tv_sec);
|
|
||||||
usecs = (endTime.tv_usec - startTime.tv_usec);
|
|
||||||
elapsed = secs + usecs / 1000000.0;
|
|
||||||
double sha256Speed = iterations / elapsed;
|
|
||||||
fprintf( stdout, " done. %d iterations in %llds %lldµs -> %.2f/s\n", iterations, secs, usecs, sha256Speed );
|
|
||||||
|
|
||||||
// Start BCrypt
|
// Start BCrypt
|
||||||
if (gettimeofday(&startTime, NULL) != 0) {
|
|
||||||
fprintf(stderr, "Could not get time: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bcrypt_cost = 9;
|
int bcrypt_cost = 9;
|
||||||
iterations = 600;
|
iterations = 600;
|
||||||
|
mpw_getTime( &startTime );
|
||||||
for (int i = 0; i < iterations; ++i) {
|
for (int i = 0; i < iterations; ++i) {
|
||||||
crypt(masterPassword, crypt_gensalt("$2b$", bcrypt_cost, userName, strlen(userName)));
|
crypt( masterPassword, crypt_gensalt( "$2b$", bcrypt_cost, fullName, strlen( fullName ) ) );
|
||||||
|
|
||||||
if (i % 10 == 0)
|
if (i % 10 == 0)
|
||||||
fprintf( stderr, "\rbcrypt (cost %d): iteration %d / %d..", bcrypt_cost, i, iterations );
|
fprintf( stderr, "\rbcrypt (cost %d): iteration %d / %d..", bcrypt_cost, i, iterations );
|
||||||
}
|
}
|
||||||
|
const double bcrypt9Speed = mpw_showSpeed( startTime, iterations, "bcrypt9" );
|
||||||
// Output timing results.
|
|
||||||
if (gettimeofday(&endTime, NULL) != 0) {
|
|
||||||
fprintf(stderr, "Could not get time: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
secs = (endTime.tv_sec - startTime.tv_sec);
|
|
||||||
usecs = (endTime.tv_usec - startTime.tv_usec);
|
|
||||||
elapsed = secs + usecs / 1000000.0;
|
|
||||||
double bcrypt9Speed = iterations / elapsed;
|
|
||||||
fprintf( stdout, " done. %d iterations in %llds %lldµs -> %.2f/s\n", iterations, secs, usecs, bcrypt9Speed );
|
|
||||||
|
|
||||||
// Summarize.
|
// Summarize.
|
||||||
fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" );
|
fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" );
|
||||||
fprintf( stdout, "mpw is %f times slower than sha256\n", sha256Speed / mpwSpeed );
|
fprintf( stdout, " - mpw is %f times slower than sha256.\n", sha256Speed / mpwSpeed );
|
||||||
fprintf( stdout, "mpw is %f times slower than bcrypt (cost 9)\n", bcrypt9Speed / mpwSpeed );
|
fprintf( stdout, " - mpw is %f times slower than bcrypt (cost 9).\n", bcrypt9Speed / mpwSpeed );
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
222
MasterPassword/C/mpw-cli.c
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#if defined(READLINE)
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#elif defined(EDITLINE)
|
||||||
|
#include <histedit.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
|
||||||
|
|
||||||
|
#include "mpw-algorithm.h"
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
#define MP_env_fullname "MP_FULLNAME"
|
||||||
|
#define MP_env_sitetype "MP_SITETYPE"
|
||||||
|
#define MP_env_sitecounter "MP_SITECOUNTER"
|
||||||
|
#define MP_env_algorithm "MP_ALGORITHM"
|
||||||
|
|
||||||
|
static void usage() {
|
||||||
|
|
||||||
|
fprintf( stderr, "Usage: mpw [-u name] [-t type] [-c counter] site\n\n" );
|
||||||
|
fprintf( stderr, " -u name Specify the full name of the user.\n"
|
||||||
|
" Defaults to %s in env.\n\n", MP_env_fullname );
|
||||||
|
fprintf( stderr, " -t type Specify the password's template.\n"
|
||||||
|
" Defaults to %s in env or 'long' for password, 'name' for login.\n"
|
||||||
|
" x, max, maximum | 20 characters, contains symbols.\n"
|
||||||
|
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
|
||||||
|
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
|
||||||
|
" b, basic | 8 characters, no symbols.\n"
|
||||||
|
" s, short | Copy-friendly, 4 characters, no symbols.\n"
|
||||||
|
" i, pin | 4 numbers.\n"
|
||||||
|
" n, name | 9 letter name.\n"
|
||||||
|
" p, phrase | 20 character sentence.\n\n", MP_env_sitetype );
|
||||||
|
fprintf( stderr, " -c counter The value of the counter.\n"
|
||||||
|
" Defaults to %s in env or 1.\n\n", MP_env_sitecounter );
|
||||||
|
fprintf( stderr, " -V version The algorithm version to use.\n"
|
||||||
|
" Defaults to %s in env or %d.\n\n", MP_env_algorithm, MPAlgorithmVersionCurrent );
|
||||||
|
fprintf( stderr, " -v variant The kind of content to generate.\n"
|
||||||
|
" Defaults to 'password'.\n"
|
||||||
|
" p, password | The password to log in with.\n"
|
||||||
|
" l, login | The username to log in as.\n"
|
||||||
|
" a, answer | The answer to a security question.\n\n" );
|
||||||
|
fprintf( stderr, " -C context A variant-specific context.\n"
|
||||||
|
" Defaults to empty.\n"
|
||||||
|
" -v p, password | Doesn't currently use a context.\n"
|
||||||
|
" -v l, login | Doesn't currently use a context.\n"
|
||||||
|
" -v a, answer | Empty for a universal site answer or\n"
|
||||||
|
" | the most significant word(s) of the question.\n\n" );
|
||||||
|
fprintf( stderr, " ENVIRONMENT\n\n"
|
||||||
|
" MP_FULLNAME | The full name of the user.\n"
|
||||||
|
" MP_SITETYPE | The default password template.\n"
|
||||||
|
" MP_SITECOUNTER | The default counter value.\n\n" );
|
||||||
|
exit( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *homedir(const char *filename) {
|
||||||
|
|
||||||
|
char *homedir = NULL;
|
||||||
|
struct passwd *passwd = getpwuid( getuid() );
|
||||||
|
if (passwd)
|
||||||
|
homedir = passwd->pw_dir;
|
||||||
|
if (!homedir)
|
||||||
|
homedir = getenv( "HOME" );
|
||||||
|
if (!homedir)
|
||||||
|
homedir = getcwd( NULL, 0 );
|
||||||
|
|
||||||
|
char *homefile = NULL;
|
||||||
|
asprintf( &homefile, "%s/%s", homedir, filename );
|
||||||
|
return homefile;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *getlinep(const char *prompt) {
|
||||||
|
|
||||||
|
char *buf = NULL;
|
||||||
|
size_t bufSize = 0;
|
||||||
|
ssize_t lineSize;
|
||||||
|
fprintf( stderr, "%s", prompt );
|
||||||
|
fprintf( stderr, " " );
|
||||||
|
if ((lineSize = getline( &buf, &bufSize, stdin )) < 0) {
|
||||||
|
free( buf );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buf[lineSize - 1] = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *const argv[]) {
|
||||||
|
|
||||||
|
// Read the environment.
|
||||||
|
char *fullName = getenv( MP_env_fullname );
|
||||||
|
const char *masterPassword = NULL;
|
||||||
|
const char *siteName = NULL;
|
||||||
|
MPSiteType siteType = MPSiteTypeGeneratedLong;
|
||||||
|
const char *siteTypeString = getenv( MP_env_sitetype );
|
||||||
|
MPSiteVariant siteVariant = MPSiteVariantPassword;
|
||||||
|
const char *siteVariantString = NULL;
|
||||||
|
const char *siteContextString = NULL;
|
||||||
|
uint32_t siteCounter = 1;
|
||||||
|
const char *siteCounterString = getenv( MP_env_sitecounter );
|
||||||
|
MPAlgorithmVersion algorithmVersion = MPAlgorithmVersionCurrent;
|
||||||
|
const char *algorithmVersionString = getenv( MP_env_algorithm );
|
||||||
|
if (algorithmVersionString && strlen( algorithmVersionString ))
|
||||||
|
if (sscanf( algorithmVersionString, "%u", &algorithmVersion ) != 1)
|
||||||
|
ftl( "Invalid %s: %s\n", MP_env_algorithm, algorithmVersionString );
|
||||||
|
|
||||||
|
// Read the options.
|
||||||
|
for (int opt; (opt = getopt( argc, argv, "u:P:t:c:v:V:C:h" )) != -1;)
|
||||||
|
switch (opt) {
|
||||||
|
case 'u':
|
||||||
|
fullName = optarg;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
// Do not use this. Passing your master password via the command-line
|
||||||
|
// is insecure. This is here for non-interactive testing purposes only.
|
||||||
|
masterPassword = strcpy( malloc( strlen( optarg ) + 1 ), optarg );
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
siteTypeString = optarg;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
siteCounterString = optarg;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
siteVariantString = optarg;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
if (sscanf( optarg, "%u", &algorithmVersion ) != 1)
|
||||||
|
ftl( "Not a version: %s\n", optarg );
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
siteContextString = optarg;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
usage();
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
switch (optopt) {
|
||||||
|
case 'u':
|
||||||
|
ftl( "Missing full name to option: -%c\n", optopt );
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
ftl( "Missing type name to option: -%c\n", optopt );
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
ftl( "Missing counter value to option: -%c\n", optopt );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ftl( "Unknown option: -%c\n", optopt );
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ftl("Unexpected option: %c", opt);
|
||||||
|
}
|
||||||
|
if (optind < argc)
|
||||||
|
siteName = argv[optind];
|
||||||
|
|
||||||
|
// Convert and validate input.
|
||||||
|
if (!fullName && !(fullName = getlinep( "Your full name:" )))
|
||||||
|
ftl( "Missing full name.\n" );
|
||||||
|
if (!siteName && !(siteName = getlinep( "Site name:" )))
|
||||||
|
ftl( "Missing site name.\n" );
|
||||||
|
if (siteCounterString)
|
||||||
|
siteCounter = (uint32_t)atol( siteCounterString );
|
||||||
|
if (siteCounter < 1)
|
||||||
|
ftl( "Invalid site counter: %d\n", siteCounter );
|
||||||
|
if (siteVariantString)
|
||||||
|
siteVariant = mpw_variantWithName( siteVariantString );
|
||||||
|
if (siteVariant == MPSiteVariantLogin)
|
||||||
|
siteType = MPSiteTypeGeneratedName;
|
||||||
|
if (siteVariant == MPSiteVariantAnswer)
|
||||||
|
siteType = MPSiteTypeGeneratedPhrase;
|
||||||
|
if (siteTypeString)
|
||||||
|
siteType = mpw_typeWithName( siteTypeString );
|
||||||
|
trc( "algorithmVersion: %u\n", algorithmVersion );
|
||||||
|
|
||||||
|
// Read the master password.
|
||||||
|
char *mpwConfigPath = homedir( ".mpw" );
|
||||||
|
if (!mpwConfigPath)
|
||||||
|
ftl( "Couldn't resolve path for configuration file: %d\n", errno );
|
||||||
|
trc( "mpwConfigPath: %s\n", mpwConfigPath );
|
||||||
|
FILE *mpwConfig = fopen( mpwConfigPath, "r" );
|
||||||
|
free( mpwConfigPath );
|
||||||
|
if (mpwConfig) {
|
||||||
|
char *line = NULL;
|
||||||
|
size_t linecap = 0;
|
||||||
|
while (getline( &line, &linecap, mpwConfig ) > 0) {
|
||||||
|
char *lineData = line;
|
||||||
|
if (strcmp( strsep( &lineData, ":" ), fullName ) == 0) {
|
||||||
|
masterPassword = strcpy( malloc( strlen( lineData ) ), strsep( &lineData, "\n" ) );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mpw_free( line, linecap );
|
||||||
|
}
|
||||||
|
while (!masterPassword || !strlen(masterPassword))
|
||||||
|
masterPassword = getpass( "Your master password: " );
|
||||||
|
|
||||||
|
// Summarize operation.
|
||||||
|
fprintf( stderr, "%s's password for %s:\n[ %s ]: ", fullName, siteName, mpw_identicon( fullName, masterPassword ) );
|
||||||
|
|
||||||
|
// Output the password.
|
||||||
|
const uint8_t *masterKey = mpw_masterKeyForUser(
|
||||||
|
fullName, masterPassword, algorithmVersion );
|
||||||
|
mpw_freeString( masterPassword );
|
||||||
|
if (!masterKey)
|
||||||
|
ftl( "Couldn't derive master key." );
|
||||||
|
|
||||||
|
const char *sitePassword = mpw_passwordForSite(
|
||||||
|
masterKey, siteName, siteType, siteCounter, siteVariant, siteContextString, algorithmVersion );
|
||||||
|
mpw_free( masterKey, MP_dkLen );
|
||||||
|
if (!sitePassword)
|
||||||
|
ftl( "Couldn't derive site password." );
|
||||||
|
|
||||||
|
fprintf( stdout, "%s\n", sitePassword );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
76
MasterPassword/C/mpw-tests-util.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// mpw-tests-util.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-21.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
#include "mpw-tests-util.h"
|
||||||
|
|
||||||
|
static xmlChar const *mpw_xmlPath(xmlNodePtr context) {
|
||||||
|
|
||||||
|
if (context->parent) {
|
||||||
|
char *string = calloc( 256, 1 );
|
||||||
|
snprintf( string, 256, "%s/%s", mpw_xmlPath( context->parent ), context->name );
|
||||||
|
return BAD_CAST string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return context->name? context->name: (xmlChar const *)"";
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlNodePtr mpw_xmlTestCaseNode(xmlNodePtr testCaseNode, const char *nodeName) {
|
||||||
|
|
||||||
|
// Try to find an attribute node.
|
||||||
|
for (xmlAttrPtr child = testCaseNode->properties; child; child = child->next)
|
||||||
|
if (xmlStrcmp( child->name, BAD_CAST nodeName ) == 0)
|
||||||
|
return (xmlNodePtr)child;
|
||||||
|
|
||||||
|
// Try to find an element node.
|
||||||
|
for (xmlNodePtr child = testCaseNode->children; child; child = child->next)
|
||||||
|
if (xmlStrcmp( child->name, BAD_CAST nodeName ) == 0)
|
||||||
|
return child;
|
||||||
|
|
||||||
|
// Missing content, try to find parent case.
|
||||||
|
if (strcmp(nodeName, "parent") == 0)
|
||||||
|
// Was just searching for testCaseNode's parent, none found.
|
||||||
|
return NULL;
|
||||||
|
xmlChar *parentId = mpw_xmlTestCaseString( testCaseNode, "parent" );
|
||||||
|
if (!parentId)
|
||||||
|
// testCaseNode has no parent, give up.
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (xmlNodePtr otherTestCaseNode = testCaseNode->parent->children; otherTestCaseNode; otherTestCaseNode = otherTestCaseNode->next) {
|
||||||
|
xmlChar *id = mpw_xmlTestCaseString( otherTestCaseNode, "id" );
|
||||||
|
int foundParent = xmlStrcmp( id, parentId ) == 0;
|
||||||
|
xmlFree( id );
|
||||||
|
|
||||||
|
if (foundParent) {
|
||||||
|
xmlFree( parentId );
|
||||||
|
return mpw_xmlTestCaseNode( otherTestCaseNode, nodeName );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ftl( "Missing parent: %s, for case: %s\n", parentId, mpw_xmlTestCaseString( testCaseNode, "id" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlChar *mpw_xmlTestCaseString(xmlNodePtr context, const char *nodeName) {
|
||||||
|
|
||||||
|
xmlNodePtr child = mpw_xmlTestCaseNode( context, nodeName );
|
||||||
|
return xmlNodeGetContent( child );
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mpw_xmlTestCaseInteger(xmlNodePtr context, const char *nodeName) {
|
||||||
|
|
||||||
|
xmlChar *string = mpw_xmlTestCaseString( context, nodeName );
|
||||||
|
uint32_t integer = atol( (char *)string );
|
||||||
|
xmlFree( string );
|
||||||
|
|
||||||
|
return integer;
|
||||||
|
}
|
||||||
16
MasterPassword/C/mpw-tests-util.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// mpw-tests-util.h
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-21.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
|
||||||
|
xmlNodePtr mpw_xmlTestCaseNode(
|
||||||
|
xmlNodePtr testCaseNode, const char *nodeName);
|
||||||
|
xmlChar *mpw_xmlTestCaseString(
|
||||||
|
xmlNodePtr context, const char *nodeName);
|
||||||
|
uint32_t mpw_xmlTestCaseInteger(
|
||||||
|
xmlNodePtr context, const char *nodeName);
|
||||||
76
MasterPassword/C/mpw-tests.c
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); exit(2); } while (0)
|
||||||
|
|
||||||
|
#include "mpw-algorithm.h"
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
#include "mpw-tests-util.h"
|
||||||
|
|
||||||
|
int main(int argc, char *const argv[]) {
|
||||||
|
|
||||||
|
int failedTests = 0;
|
||||||
|
|
||||||
|
xmlNodePtr tests = xmlDocGetRootElement( xmlParseFile( "mpw_tests.xml" ) );
|
||||||
|
for (xmlNodePtr testCase = tests->children; testCase; testCase = testCase->next) {
|
||||||
|
if (testCase->type != XML_ELEMENT_NODE || xmlStrcmp( testCase->name, BAD_CAST "case" ) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Read in the test case.
|
||||||
|
xmlChar *id = mpw_xmlTestCaseString( testCase, "id" );
|
||||||
|
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
|
||||||
|
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
|
||||||
|
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
|
||||||
|
xmlChar *siteName = mpw_xmlTestCaseString( testCase, "siteName" );
|
||||||
|
uint32_t siteCounter = mpw_xmlTestCaseInteger( testCase, "siteCounter" );
|
||||||
|
xmlChar *siteTypeString = mpw_xmlTestCaseString( testCase, "siteType" );
|
||||||
|
xmlChar *siteVariantString = mpw_xmlTestCaseString( testCase, "siteVariant" );
|
||||||
|
xmlChar *siteContext = mpw_xmlTestCaseString( testCase, "siteContext" );
|
||||||
|
xmlChar *result = mpw_xmlTestCaseString( testCase, "result" );
|
||||||
|
|
||||||
|
MPSiteType siteType = mpw_typeWithName( (char *)siteTypeString );
|
||||||
|
MPSiteVariant siteVariant = mpw_variantWithName( (char *)siteVariantString );
|
||||||
|
|
||||||
|
// Run the test case.
|
||||||
|
fprintf( stdout, "test case %s... ", id );
|
||||||
|
|
||||||
|
// 1. calculate the master key.
|
||||||
|
const uint8_t *masterKey = mpw_masterKeyForUser(
|
||||||
|
(char *)fullName, (char *)masterPassword, MPAlgorithmVersionCurrent );
|
||||||
|
if (!masterKey)
|
||||||
|
ftl( "Couldn't derive master key." );
|
||||||
|
|
||||||
|
// 2. calculate the site password.
|
||||||
|
const char *sitePassword = mpw_passwordForSite(
|
||||||
|
masterKey, (char *)siteName, siteType, siteCounter, siteVariant, (char *)siteContext, MPAlgorithmVersionCurrent );
|
||||||
|
mpw_free( masterKey, MP_dkLen );
|
||||||
|
if (!sitePassword)
|
||||||
|
ftl( "Couldn't derive site password." );
|
||||||
|
|
||||||
|
// Check the result.
|
||||||
|
if (xmlStrcmp( result, BAD_CAST sitePassword ) == 0)
|
||||||
|
fprintf( stdout, "pass.\n" );
|
||||||
|
|
||||||
|
else {
|
||||||
|
++failedTests;
|
||||||
|
fprintf( stdout, "FAILED! (result %s != expected %s)\n", result, sitePassword );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free test case.
|
||||||
|
mpw_freeString( sitePassword );
|
||||||
|
xmlFree( id );
|
||||||
|
xmlFree( fullName );
|
||||||
|
xmlFree( masterPassword );
|
||||||
|
xmlFree( keyID );
|
||||||
|
xmlFree( siteName );
|
||||||
|
xmlFree( siteTypeString );
|
||||||
|
xmlFree( siteVariantString );
|
||||||
|
xmlFree( siteContext );
|
||||||
|
xmlFree( result );
|
||||||
|
}
|
||||||
|
|
||||||
|
return failedTests;
|
||||||
|
}
|
||||||
190
MasterPassword/C/mpw-types.c
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
//
|
||||||
|
// mpw-types.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2012-02-01.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#ifdef COLOR
|
||||||
|
#include <curses.h>
|
||||||
|
#include <term.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "mpw-types.h"
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
const MPSiteType mpw_typeWithName(const char *typeName) {
|
||||||
|
|
||||||
|
size_t stdTypeNameSize = strlen( typeName );
|
||||||
|
char stdTypeName[strlen( typeName )];
|
||||||
|
if (stdTypeNameSize > strlen( "generated" ))
|
||||||
|
strcpy( stdTypeName, typeName + strlen( "generated" ) );
|
||||||
|
else
|
||||||
|
strcpy( stdTypeName, typeName );
|
||||||
|
for (char *tN = stdTypeName; *tN; ++tN)
|
||||||
|
*tN = (char)tolower( *tN );
|
||||||
|
|
||||||
|
if (0 == strcmp( stdTypeName, "x" ) || 0 == strcmp( stdTypeName, "max" ) || 0 == strcmp( stdTypeName, "maximum" ))
|
||||||
|
return MPSiteTypeGeneratedMaximum;
|
||||||
|
if (0 == strcmp( stdTypeName, "l" ) || 0 == strcmp( stdTypeName, "long" ))
|
||||||
|
return MPSiteTypeGeneratedLong;
|
||||||
|
if (0 == strcmp( stdTypeName, "m" ) || 0 == strcmp( stdTypeName, "med" ) || 0 == strcmp( stdTypeName, "medium" ))
|
||||||
|
return MPSiteTypeGeneratedMedium;
|
||||||
|
if (0 == strcmp( stdTypeName, "b" ) || 0 == strcmp( stdTypeName, "basic" ))
|
||||||
|
return MPSiteTypeGeneratedBasic;
|
||||||
|
if (0 == strcmp( stdTypeName, "s" ) || 0 == strcmp( stdTypeName, "short" ))
|
||||||
|
return MPSiteTypeGeneratedShort;
|
||||||
|
if (0 == strcmp( stdTypeName, "i" ) || 0 == strcmp( stdTypeName, "pin" ))
|
||||||
|
return MPSiteTypeGeneratedPIN;
|
||||||
|
if (0 == strcmp( stdTypeName, "n" ) || 0 == strcmp( stdTypeName, "name" ))
|
||||||
|
return MPSiteTypeGeneratedName;
|
||||||
|
if (0 == strcmp( stdTypeName, "p" ) || 0 == strcmp( stdTypeName, "phrase" ))
|
||||||
|
return MPSiteTypeGeneratedPhrase;
|
||||||
|
|
||||||
|
fprintf( stderr, "Not a generated type name: %s", stdTypeName );
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char **mpw_templatesForType(MPSiteType type, size_t *count) {
|
||||||
|
|
||||||
|
if (!(type & MPSiteTypeClassGenerated)) {
|
||||||
|
ftl( "Not a generated type: %d", type );
|
||||||
|
*count = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case MPSiteTypeGeneratedMaximum: {
|
||||||
|
*count = 2;
|
||||||
|
return (const char *[]){ "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
|
||||||
|
}
|
||||||
|
case MPSiteTypeGeneratedLong: {
|
||||||
|
*count = 21;
|
||||||
|
return (const char *[]){ "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
|
||||||
|
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
|
||||||
|
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
|
||||||
|
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
|
||||||
|
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
|
||||||
|
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
|
||||||
|
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" };
|
||||||
|
}
|
||||||
|
case MPSiteTypeGeneratedMedium: {
|
||||||
|
*count = 2;
|
||||||
|
return (const char *[]){ "CvcnoCvc", "CvcCvcno" };
|
||||||
|
}
|
||||||
|
case MPSiteTypeGeneratedBasic: {
|
||||||
|
*count = 3;
|
||||||
|
return (const char *[]){ "aaanaaan", "aannaaan", "aaannaaa" };
|
||||||
|
}
|
||||||
|
case MPSiteTypeGeneratedShort: {
|
||||||
|
*count = 1;
|
||||||
|
return (const char *[]){"Cvcn"};
|
||||||
|
}
|
||||||
|
case MPSiteTypeGeneratedPIN: {
|
||||||
|
*count = 1;
|
||||||
|
return (const char *[]){ "nnnn" };
|
||||||
|
}
|
||||||
|
case MPSiteTypeGeneratedName: {
|
||||||
|
*count = 1;
|
||||||
|
return (const char *[]) {"cvccvcvcv"};
|
||||||
|
}
|
||||||
|
case MPSiteTypeGeneratedPhrase: {
|
||||||
|
*count = 3;
|
||||||
|
return (const char *[]){ "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
ftl( "Unknown generated type: %d", type );
|
||||||
|
*count = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte) {
|
||||||
|
|
||||||
|
size_t count = 0;
|
||||||
|
const char **templates = mpw_templatesForType( type, &count );
|
||||||
|
if (!count)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return templates[seedByte % count];
|
||||||
|
}
|
||||||
|
|
||||||
|
const MPSiteVariant mpw_variantWithName(const char *variantName) {
|
||||||
|
|
||||||
|
char stdVariantName[strlen( variantName )];
|
||||||
|
strcpy( stdVariantName, variantName );
|
||||||
|
for (char *vN = stdVariantName; *vN; ++vN)
|
||||||
|
*vN = (char)tolower( *vN );
|
||||||
|
|
||||||
|
if (0 == strcmp( stdVariantName, "p" ) || 0 == strcmp( stdVariantName, "password" ))
|
||||||
|
return MPSiteVariantPassword;
|
||||||
|
if (0 == strcmp( stdVariantName, "l" ) || 0 == strcmp( stdVariantName, "login" ))
|
||||||
|
return MPSiteVariantLogin;
|
||||||
|
if (0 == strcmp( stdVariantName, "a" ) || 0 == strcmp( stdVariantName, "answer" ))
|
||||||
|
return MPSiteVariantAnswer;
|
||||||
|
|
||||||
|
fprintf( stderr, "Not a variant name: %s", stdVariantName );
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mpw_scopeForVariant(MPSiteVariant variant) {
|
||||||
|
|
||||||
|
switch (variant) {
|
||||||
|
case MPSiteVariantPassword: {
|
||||||
|
return "com.lyndir.masterpassword";
|
||||||
|
}
|
||||||
|
case MPSiteVariantLogin: {
|
||||||
|
return "com.lyndir.masterpassword.login";
|
||||||
|
}
|
||||||
|
case MPSiteVariantAnswer: {
|
||||||
|
return "com.lyndir.masterpassword.answer";
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
fprintf( stderr, "Unknown variant: %d", variant );
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mpw_charactersInClass(char characterClass) {
|
||||||
|
|
||||||
|
switch (characterClass) {
|
||||||
|
case 'V':
|
||||||
|
return "AEIOU";
|
||||||
|
case 'C':
|
||||||
|
return "BCDFGHJKLMNPQRSTVWXYZ";
|
||||||
|
case 'v':
|
||||||
|
return "aeiou";
|
||||||
|
case 'c':
|
||||||
|
return "bcdfghjklmnpqrstvwxyz";
|
||||||
|
case 'A':
|
||||||
|
return "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
|
||||||
|
case 'a':
|
||||||
|
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
|
||||||
|
case 'n':
|
||||||
|
return "0123456789";
|
||||||
|
case 'o':
|
||||||
|
return "@&%?,=[]_:-+*$#!'^~;()/.";
|
||||||
|
case 'x':
|
||||||
|
return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
|
||||||
|
case ' ':
|
||||||
|
return " ";
|
||||||
|
default: {
|
||||||
|
fprintf( stderr, "Unknown character class: %c", characterClass );
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
|
||||||
|
|
||||||
|
const char *classCharacters = mpw_charactersInClass( characterClass );
|
||||||
|
return classCharacters[seedByte % strlen( classCharacters )];
|
||||||
|
}
|
||||||
95
MasterPassword/C/mpw-types.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// mpw-types.h
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2012-02-01.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef NS_ENUM
|
||||||
|
#define enum(_type, _name) NS_ENUM(_type, _name)
|
||||||
|
#else
|
||||||
|
#define enum(_type, _name) _type _name; enum
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MP_dkLen 64
|
||||||
|
|
||||||
|
//// Types.
|
||||||
|
|
||||||
|
typedef enum( unsigned int, MPSiteVariant ) {
|
||||||
|
/** Generate the password to log in with. */
|
||||||
|
MPSiteVariantPassword,
|
||||||
|
/** Generate the login name to log in as. */
|
||||||
|
MPSiteVariantLogin,
|
||||||
|
/** Generate the answer to a security question. */
|
||||||
|
MPSiteVariantAnswer,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum( unsigned int, MPSiteTypeClass ) {
|
||||||
|
/** Generate the password. */
|
||||||
|
MPSiteTypeClassGenerated = 1 << 4,
|
||||||
|
/** Store the password. */
|
||||||
|
MPSiteTypeClassStored = 1 << 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum( unsigned int, MPSiteFeature ) {
|
||||||
|
/** Export the key-protected content data. */
|
||||||
|
MPSiteFeatureExportContent = 1 << 10,
|
||||||
|
/** Never export content. */
|
||||||
|
MPSiteFeatureDevicePrivate = 1 << 11,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum( unsigned int, MPSiteType) {
|
||||||
|
MPSiteTypeGeneratedMaximum = 0x0 | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedLong = 0x1 | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedMedium = 0x2 | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedBasic = 0x4 | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedShort = 0x3 | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedPIN = 0x5 | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedName = 0xE | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
MPSiteTypeGeneratedPhrase = 0xF | MPSiteTypeClassGenerated | 0x0,
|
||||||
|
|
||||||
|
MPSiteTypeStoredPersonal = 0x0 | MPSiteTypeClassStored | MPSiteFeatureExportContent,
|
||||||
|
MPSiteTypeStoredDevicePrivate = 0x1 | MPSiteTypeClassStored | MPSiteFeatureDevicePrivate,
|
||||||
|
};
|
||||||
|
|
||||||
|
//// Type utilities.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The variant represented by the given name.
|
||||||
|
*/
|
||||||
|
const MPSiteVariant mpw_variantWithName(const char *variantName);
|
||||||
|
/**
|
||||||
|
* @return An internal string containing the scope identifier to apply when encoding for the given variant.
|
||||||
|
*/
|
||||||
|
const char *mpw_scopeForVariant(MPSiteVariant variant);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The type represented by the given name.
|
||||||
|
*/
|
||||||
|
const MPSiteType mpw_typeWithName(const char *typeName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An array of internal strings that express the templates to use for the given type.
|
||||||
|
* The amount of elements in the array is stored in count.
|
||||||
|
* If an unsupported type is given, count will be 0 and will return NULL.
|
||||||
|
*/
|
||||||
|
const char **mpw_templatesForType(MPSiteType type, size_t *count);
|
||||||
|
/**
|
||||||
|
* @return An internal string that contains the password encoding template of the given type
|
||||||
|
* for a seed that starts with the given byte.
|
||||||
|
*/
|
||||||
|
const char *mpw_templateForType(MPSiteType type, uint8_t seedByte);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An internal string that contains all the characters that occur in the given character class.
|
||||||
|
*/
|
||||||
|
const char *mpw_charactersInClass(char characterClass);
|
||||||
|
/**
|
||||||
|
* @return A character from given character class that encodes the given byte.
|
||||||
|
*/
|
||||||
|
const char mpw_characterFromClass(char characterClass, uint8_t seedByte);
|
||||||
|
|
||||||
172
MasterPassword/C/mpw-util.c
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
//
|
||||||
|
// mpw-util.c
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#include <scrypt/sha256.h>
|
||||||
|
#include <scrypt/crypto_scrypt.h>
|
||||||
|
|
||||||
|
#include "mpw-util.h"
|
||||||
|
|
||||||
|
void mpw_pushBuf(uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize) {
|
||||||
|
|
||||||
|
if (*bufferSize == (size_t)-1)
|
||||||
|
// The buffer was marked as broken, it is missing a previous push. Abort to avoid corrupt content.
|
||||||
|
return;
|
||||||
|
|
||||||
|
*bufferSize += pushSize;
|
||||||
|
uint8_t *resizedBuffer = realloc( *buffer, *bufferSize );
|
||||||
|
if (!resizedBuffer) {
|
||||||
|
// realloc failed, we can't push. Mark the buffer as broken.
|
||||||
|
mpw_free( *buffer, *bufferSize - pushSize );
|
||||||
|
*bufferSize = (size_t)-1;
|
||||||
|
*buffer = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
*buffer = resizedBuffer;
|
||||||
|
uint8_t *pushDst = *buffer + *bufferSize - pushSize;
|
||||||
|
memcpy( pushDst, pushBuffer, pushSize );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpw_pushString(uint8_t **buffer, size_t *const bufferSize, const char *pushString) {
|
||||||
|
|
||||||
|
mpw_pushBuf( buffer, bufferSize, pushString, strlen( pushString ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpw_pushInt(uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt) {
|
||||||
|
|
||||||
|
mpw_pushBuf( buffer, bufferSize, &pushInt, sizeof( pushInt ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpw_free(const void *buffer, const size_t bufferSize) {
|
||||||
|
|
||||||
|
memset( (void *)buffer, 0, bufferSize );
|
||||||
|
free( (void *)buffer );
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpw_freeString(const char *string) {
|
||||||
|
|
||||||
|
mpw_free( string, strlen( string ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t const *mpw_scrypt(const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
|
||||||
|
uint64_t N, uint32_t r, uint32_t p) {
|
||||||
|
|
||||||
|
uint8_t *key = malloc( keySize );
|
||||||
|
if (!key)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
|
||||||
|
mpw_free( key, keySize );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (!buffer)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
HMAC_SHA256_Buf( key, keySize, salt, saltSize, buffer );
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *mpw_idForBuf(const void *buf, size_t length) {
|
||||||
|
|
||||||
|
uint8_t hash[32];
|
||||||
|
SHA256_Buf( buf, length, hash );
|
||||||
|
|
||||||
|
return mpw_hex( hash, 32 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *mpw_hex_buf = NULL;
|
||||||
|
const char *mpw_hex(const void *buf, size_t length) {
|
||||||
|
|
||||||
|
mpw_hex_buf = realloc( mpw_hex_buf, length * 2 + 1 );
|
||||||
|
for (size_t kH = 0; kH < length; kH++)
|
||||||
|
sprintf( &(mpw_hex_buf[kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
|
||||||
|
|
||||||
|
return mpw_hex_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef COLOR
|
||||||
|
static int putvari;
|
||||||
|
static char *putvarc = NULL;
|
||||||
|
static bool istermsetup = false;
|
||||||
|
static void initputvar() {
|
||||||
|
if (putvarc)
|
||||||
|
free(putvarc);
|
||||||
|
putvarc=(char *)calloc(256, sizeof(char));
|
||||||
|
putvari=0;
|
||||||
|
|
||||||
|
if (!istermsetup)
|
||||||
|
istermsetup = (OK == setupterm(NULL, STDERR_FILENO, NULL));
|
||||||
|
}
|
||||||
|
static int putvar(int c) {
|
||||||
|
putvarc[putvari++]=c;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *mpw_identicon(const char *fullName, const char *masterPassword) {
|
||||||
|
|
||||||
|
const char *leftArm[] = { "╔", "╚", "╰", "═" };
|
||||||
|
const char *rightArm[] = { "╗", "╝", "╯", "═" };
|
||||||
|
const char *body[] = { "█", "░", "▒", "▓", "☺", "☻" };
|
||||||
|
const char *accessory[] = {
|
||||||
|
"◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
|
||||||
|
"☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
|
||||||
|
"♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌" };
|
||||||
|
|
||||||
|
uint8_t identiconSeed[32];
|
||||||
|
HMAC_SHA256_Buf( masterPassword, strlen( masterPassword ), fullName, strlen( fullName ), identiconSeed );
|
||||||
|
|
||||||
|
char *colorString, *resetString;
|
||||||
|
#ifdef COLOR
|
||||||
|
if (isatty( STDERR_FILENO )) {
|
||||||
|
uint8_t colorIdentifier = (uint8_t)(identiconSeed[4] % 7 + 1);
|
||||||
|
initputvar();
|
||||||
|
tputs(tparm(tgetstr("AF", NULL), colorIdentifier), 1, putvar);
|
||||||
|
colorString = calloc(strlen(putvarc) + 1, sizeof(char));
|
||||||
|
strcpy(colorString, putvarc);
|
||||||
|
tputs(tgetstr("me", NULL), 1, putvar);
|
||||||
|
resetString = calloc(strlen(putvarc) + 1, sizeof(char));
|
||||||
|
strcpy(resetString, putvarc);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
colorString = calloc( 1, sizeof( char ) );
|
||||||
|
resetString = calloc( 1, sizeof( char ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *identicon = (char *)calloc( 256, sizeof( char ) );
|
||||||
|
snprintf( identicon, 256, "%s%s%s%s%s%s",
|
||||||
|
colorString,
|
||||||
|
leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
|
||||||
|
body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
|
||||||
|
rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
|
||||||
|
accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
|
||||||
|
resetString );
|
||||||
|
|
||||||
|
free( colorString );
|
||||||
|
free( resetString );
|
||||||
|
return identicon;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t mpw_charlen(const char *string) {
|
||||||
|
|
||||||
|
setlocale( LC_ALL, "en_US.UTF-8" );
|
||||||
|
return mbstowcs( NULL, string, strlen( string ) );
|
||||||
|
}
|
||||||
70
MasterPassword/C/mpw-util.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
//
|
||||||
|
// mpw-util.h
|
||||||
|
// MasterPassword
|
||||||
|
//
|
||||||
|
// Created by Maarten Billemont on 2014-12-20.
|
||||||
|
// Copyright (c) 2014 Lyndir. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//// Logging.
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#ifndef trc
|
||||||
|
#define trc(...) fprintf( stderr, __VA_ARGS__ )
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifndef trc
|
||||||
|
#define trc(...) do {} while (0)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifndef ftl
|
||||||
|
#define ftl(...) do { fprintf( stderr, __VA_ARGS__ ); abort(); } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//// Buffers and memory.
|
||||||
|
|
||||||
|
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
|
||||||
|
void mpw_pushBuf(
|
||||||
|
uint8_t **const buffer, size_t *const bufferSize, const void *pushBuffer, const size_t pushSize);
|
||||||
|
/** Push a string onto a buffer. reallocs the given buffer and appends the given string. */
|
||||||
|
void mpw_pushString(
|
||||||
|
uint8_t **buffer, size_t *const bufferSize, const char *pushString);
|
||||||
|
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */
|
||||||
|
void mpw_pushInt(
|
||||||
|
uint8_t **const buffer, size_t *const bufferSize, const uint32_t pushInt);
|
||||||
|
/** Free a buffer after zero'ing its contents. */
|
||||||
|
void mpw_free(
|
||||||
|
const void *buffer, const size_t bufferSize);
|
||||||
|
/** Free a string after zero'ing its contents. */
|
||||||
|
void mpw_freeString(
|
||||||
|
const char *string);
|
||||||
|
|
||||||
|
//// Cryptographic functions.
|
||||||
|
|
||||||
|
/** Perform a scrypt-based key derivation on the given key using the given salt and scrypt parameters.
|
||||||
|
* @return A new keySize-size allocated buffer. */
|
||||||
|
uint8_t const *mpw_scrypt(
|
||||||
|
const size_t keySize, const char *secret, const uint8_t *salt, const size_t saltSize,
|
||||||
|
uint64_t N, uint32_t r, uint32_t p);
|
||||||
|
/** Calculate a SHA256-based HMAC by encrypting the given salt with the given key.
|
||||||
|
* @return A new 32-byte allocated buffer. */
|
||||||
|
uint8_t const *mpw_hmac_sha256(
|
||||||
|
const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize);
|
||||||
|
|
||||||
|
//// Visualizers.
|
||||||
|
|
||||||
|
/** Encode a buffer as a string of hexadecimal characters.
|
||||||
|
* @return A C-string in a reused buffer, do not free or store it. */
|
||||||
|
const char *mpw_hex(const void *buf, size_t length);
|
||||||
|
/** Encode a fingerprint for a buffer.
|
||||||
|
* @return A C-string in a reused buffer, do not free or store it. */
|
||||||
|
const char *mpw_idForBuf(const void *buf, size_t length);
|
||||||
|
/** Encode a visual fingerprint for a user.
|
||||||
|
* @return A newly allocated string. */
|
||||||
|
const char *mpw_identicon(const char *fullName, const char *masterPassword);
|
||||||
|
|
||||||
|
//// String utilities.
|
||||||
|
|
||||||
|
const size_t mpw_charlen(const char *string);
|
||||||
@@ -17,8 +17,8 @@ mpw() {
|
|||||||
:| _copy 2>/dev/null
|
:| _copy 2>/dev/null
|
||||||
|
|
||||||
# Ask for the user's name and password if not yet known.
|
# Ask for the user's name and password if not yet known.
|
||||||
MP_USERNAME=${MP_USERNAME:-$(ask 'Your Full Name:')}
|
MP_FULLNAME=${MP_FULLNAME:-$(ask 'Your Full Name:')}
|
||||||
|
|
||||||
# Start Master Password and copy the output.
|
# Start Master Password and copy the output.
|
||||||
printf %s "$(MP_USERNAME=$MP_USERNAME command mpw "$@")" | _copy
|
printf %s "$(MP_FULLNAME=$MP_FULLNAME command mpw "$@")" | _copy
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,316 +0,0 @@
|
|||||||
#define _GNU_SOURCE
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#if defined(__linux__)
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#elif defined(__CYGWIN__)
|
|
||||||
#include <cygwin/fs.h>
|
|
||||||
#else
|
|
||||||
#include <sys/disk.h>
|
|
||||||
#endif
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <alg/sha256.h>
|
|
||||||
#include <crypto/crypto_scrypt.h>
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
#if defined(READLINE)
|
|
||||||
#include <readline/readline.h>
|
|
||||||
#elif defined(EDITLINE)
|
|
||||||
#include <histedit.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MP_N 32768
|
|
||||||
#define MP_r 8
|
|
||||||
#define MP_p 2
|
|
||||||
#define MP_dkLen 64
|
|
||||||
#define MP_hash PearlHashSHA256
|
|
||||||
|
|
||||||
#define MP_env_username "MP_USERNAME"
|
|
||||||
#define MP_env_sitetype "MP_SITETYPE"
|
|
||||||
#define MP_env_sitecounter "MP_SITECOUNTER"
|
|
||||||
|
|
||||||
void usage() {
|
|
||||||
fprintf(stderr, "Usage: mpw [-u name] [-t type] [-c counter] site\n\n");
|
|
||||||
fprintf(stderr, " -u name Specify the full name of the user.\n"
|
|
||||||
" Defaults to %s in env.\n\n", MP_env_username);
|
|
||||||
fprintf(stderr, " -t type Specify the password's template.\n"
|
|
||||||
" Defaults to %s in env or 'long' for password, 'name' for login.\n"
|
|
||||||
" x, max, maximum | 20 characters, contains symbols.\n"
|
|
||||||
" l, long | Copy-friendly, 14 characters, contains symbols.\n"
|
|
||||||
" m, med, medium | Copy-friendly, 8 characters, contains symbols.\n"
|
|
||||||
" b, basic | 8 characters, no symbols.\n"
|
|
||||||
" s, short | Copy-friendly, 4 characters, no symbols.\n"
|
|
||||||
" i, pin | 4 numbers.\n"
|
|
||||||
" n, name | 9 letter name.\n"
|
|
||||||
" p, phrase | 20 character sentence.\n\n", MP_env_sitetype);
|
|
||||||
fprintf(stderr, " -c counter The value of the counter.\n"
|
|
||||||
" Defaults to %s in env or '1'.\n\n", MP_env_sitecounter);
|
|
||||||
fprintf(stderr, " -v variant The kind of content to generate.\n"
|
|
||||||
" Defaults to 'password'.\n"
|
|
||||||
" p, password | The password to log in with.\n"
|
|
||||||
" l, login | The username to log in as.\n"
|
|
||||||
" a, answer | The answer to a security question.\n\n");
|
|
||||||
fprintf(stderr, " -C context A variant-specific context.\n"
|
|
||||||
" Defaults to empty.\n"
|
|
||||||
" -v p, password | Doesn't currently use a context.\n"
|
|
||||||
" -v l, login | Doesn't currently use a context.\n"
|
|
||||||
" -v a, answer | Empty for a universal site answer or\n"
|
|
||||||
" | the most significant word(s) of the question.\n\n");
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *homedir(const char *filename) {
|
|
||||||
char *homedir = NULL;
|
|
||||||
#if defined(__CYGWIN__)
|
|
||||||
homedir = getenv("USERPROFILE");
|
|
||||||
if (!homedir) {
|
|
||||||
const char *homeDrive = getenv("HOMEDRIVE");
|
|
||||||
const char *homePath = getenv("HOMEPATH");
|
|
||||||
homedir = char[strlen(homeDrive) + strlen(homePath) + 1];
|
|
||||||
sprintf(homedir, "%s/%s", homeDrive, homePath);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
struct passwd* passwd = getpwuid(getuid());
|
|
||||||
if (passwd)
|
|
||||||
homedir = passwd->pw_dir;
|
|
||||||
if (!homedir)
|
|
||||||
homedir = getenv("HOME");
|
|
||||||
#endif
|
|
||||||
if (!homedir)
|
|
||||||
homedir = getcwd(NULL, 0);
|
|
||||||
|
|
||||||
char *homefile = NULL;
|
|
||||||
asprintf(&homefile, "%s/%s", homedir, filename);
|
|
||||||
return homefile;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *getlinep(const char *prompt) {
|
|
||||||
char *buf = NULL;
|
|
||||||
size_t bufSize = 0;
|
|
||||||
ssize_t lineSize;
|
|
||||||
fprintf(stderr, "%s", prompt);
|
|
||||||
fprintf(stderr, " ");
|
|
||||||
if ((lineSize = getline(&buf, &bufSize, stdin)) < 0) {
|
|
||||||
free(buf);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buf[lineSize - 1]=0;
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *const argv[]) {
|
|
||||||
|
|
||||||
// Read the environment.
|
|
||||||
char *userName = getenv( MP_env_username );
|
|
||||||
const char *masterPassword = NULL;
|
|
||||||
const char *siteName = NULL;
|
|
||||||
MPElementType siteType = MPElementTypeGeneratedLong;
|
|
||||||
const char *siteTypeString = getenv( MP_env_sitetype );
|
|
||||||
MPElementVariant siteVariant = MPElementVariantPassword;
|
|
||||||
const char *siteVariantString = NULL;
|
|
||||||
const char *siteContextString = NULL;
|
|
||||||
uint32_t siteCounter = 1;
|
|
||||||
const char *siteCounterString = getenv( MP_env_sitecounter );
|
|
||||||
|
|
||||||
// Read the options.
|
|
||||||
for (int opt; (opt = getopt(argc, argv, "u:t:c:v:C:h")) != -1;)
|
|
||||||
switch (opt) {
|
|
||||||
case 'u':
|
|
||||||
userName = optarg;
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
siteTypeString = optarg;
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
siteCounterString = optarg;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
siteVariantString = optarg;
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
siteContextString = optarg;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
usage();
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
switch (optopt) {
|
|
||||||
case 'u':
|
|
||||||
fprintf(stderr, "Missing user name to option: -%c\n", optopt);
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
fprintf(stderr, "Missing type name to option: -%c\n", optopt);
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
fprintf(stderr, "Missing counter value to option: -%c\n", optopt);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Unknown option: -%c\n", optopt);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
default:
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
if (optind < argc)
|
|
||||||
siteName = argv[optind];
|
|
||||||
|
|
||||||
// Convert and validate input.
|
|
||||||
if (!userName) {
|
|
||||||
if (!(userName = getlinep("Your user name:"))) {
|
|
||||||
fprintf(stderr, "Missing user name.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trc("userName: %s\n", userName);
|
|
||||||
if (!siteName) {
|
|
||||||
if (!(siteName = getlinep("Site name:"))) {
|
|
||||||
fprintf(stderr, "Missing site name.\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trc("siteName: %s\n", siteName);
|
|
||||||
if (siteCounterString)
|
|
||||||
siteCounter = atoi( siteCounterString );
|
|
||||||
if (siteCounter < 1) {
|
|
||||||
fprintf(stderr, "Invalid site counter: %d\n", siteCounter);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
trc("siteCounter: %d\n", siteCounter);
|
|
||||||
if (siteVariantString)
|
|
||||||
siteVariant = VariantWithName( siteVariantString );
|
|
||||||
trc("siteVariant: %d (%s)\n", siteVariant, siteVariantString);
|
|
||||||
if (siteVariant == MPElementVariantLogin)
|
|
||||||
siteType = MPElementTypeGeneratedName;
|
|
||||||
if (siteVariant == MPElementVariantAnswer)
|
|
||||||
siteType = MPElementTypeGeneratedPhrase;
|
|
||||||
if (siteTypeString)
|
|
||||||
siteType = TypeWithName( siteTypeString );
|
|
||||||
trc("siteType: %d (%s)\n", siteType, siteTypeString);
|
|
||||||
|
|
||||||
// Read the master password.
|
|
||||||
char *mpwConfigPath = homedir(".mpw");
|
|
||||||
if (!mpwConfigPath) {
|
|
||||||
fprintf(stderr, "Couldn't resolve path for configuration file: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
trc("mpwConfigPath: %s\n", mpwConfigPath);
|
|
||||||
FILE *mpwConfig = fopen(mpwConfigPath, "r");
|
|
||||||
free(mpwConfigPath);
|
|
||||||
if (mpwConfig) {
|
|
||||||
char *line = NULL;
|
|
||||||
size_t linecap = 0;
|
|
||||||
ssize_t linelen;
|
|
||||||
while ((linelen = getline(&line, &linecap, mpwConfig)) > 0)
|
|
||||||
if (strcmp(strsep(&line, ":"), userName) == 0) {
|
|
||||||
masterPassword = strsep(&line, "\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (!masterPassword)
|
|
||||||
masterPassword = getpass( "Your master password: " );
|
|
||||||
trc("masterPassword: %s\n", masterPassword);
|
|
||||||
|
|
||||||
// Summarize operation.
|
|
||||||
fprintf(stderr, "%s's password for %s:\n[ %s ]: ", userName, siteName, Identicon( userName, masterPassword ));
|
|
||||||
|
|
||||||
// Calculate the master key salt.
|
|
||||||
const char *mpKeyScope = ScopeForVariant(MPElementVariantPassword);
|
|
||||||
trc("key scope: %s\n", mpKeyScope);
|
|
||||||
const uint32_t n_userNameLength = htonl(strlen(userName));
|
|
||||||
const size_t masterKeySaltLength = strlen(mpKeyScope) + sizeof(n_userNameLength) + strlen(userName);
|
|
||||||
char *masterKeySalt = (char *)malloc( masterKeySaltLength );
|
|
||||||
if (!masterKeySalt) {
|
|
||||||
fprintf(stderr, "Could not allocate master key salt: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *mKS = masterKeySalt;
|
|
||||||
memcpy(mKS, mpKeyScope, strlen(mpKeyScope)); mKS += strlen(mpKeyScope);
|
|
||||||
memcpy(mKS, &n_userNameLength, sizeof(n_userNameLength)); mKS += sizeof(n_userNameLength);
|
|
||||||
memcpy(mKS, userName, strlen(userName)); mKS += strlen(userName);
|
|
||||||
if (mKS - masterKeySalt != masterKeySaltLength)
|
|
||||||
abort();
|
|
||||||
trc("masterKeySalt ID: %s\n", IDForBuf(masterKeySalt, masterKeySaltLength));
|
|
||||||
|
|
||||||
// Calculate the master key.
|
|
||||||
uint8_t *masterKey = (uint8_t *)malloc( MP_dkLen );
|
|
||||||
if (!masterKey) {
|
|
||||||
fprintf(stderr, "Could not allocate master key: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (crypto_scrypt( (const uint8_t *)masterPassword, strlen(masterPassword), (const uint8_t *)masterKeySalt, masterKeySaltLength, MP_N, MP_r, MP_p, masterKey, MP_dkLen ) < 0) {
|
|
||||||
fprintf(stderr, "Could not generate master key: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
memset(masterKeySalt, 0, masterKeySaltLength);
|
|
||||||
free(masterKeySalt);
|
|
||||||
trc("masterPassword Hex: %s\n", Hex(masterPassword, strlen(masterPassword)));
|
|
||||||
trc("masterPassword ID: %s\n", IDForBuf(masterPassword, strlen(masterPassword)));
|
|
||||||
trc("masterKey ID: %s\n", IDForBuf(masterKey, MP_dkLen));
|
|
||||||
|
|
||||||
// Calculate the site seed.
|
|
||||||
const char *mpSiteScope = ScopeForVariant(siteVariant);
|
|
||||||
trc("site scope: %s, context: %s\n", mpSiteScope, siteContextString == NULL? "<empty>": siteContextString);
|
|
||||||
const uint32_t n_siteNameLength = htonl(strlen(siteName));
|
|
||||||
const uint32_t n_siteCounter = htonl(siteCounter);
|
|
||||||
const uint32_t n_siteContextLength = siteContextString == NULL? 0: htonl(strlen(siteContextString));
|
|
||||||
size_t sitePasswordInfoLength = strlen(mpSiteScope) + sizeof(n_siteNameLength) + strlen(siteName) + sizeof(n_siteCounter);
|
|
||||||
if (siteContextString)
|
|
||||||
sitePasswordInfoLength += sizeof(n_siteContextLength) + strlen(siteContextString);
|
|
||||||
char *sitePasswordInfo = (char *)malloc( sitePasswordInfoLength );
|
|
||||||
if (!sitePasswordInfo) {
|
|
||||||
fprintf(stderr, "Could not allocate site seed: %d\n", errno);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *sPI = sitePasswordInfo;
|
|
||||||
memcpy(sPI, mpSiteScope, strlen(mpSiteScope)); sPI += strlen(mpSiteScope);
|
|
||||||
memcpy(sPI, &n_siteNameLength, sizeof(n_siteNameLength)); sPI += sizeof(n_siteNameLength);
|
|
||||||
memcpy(sPI, siteName, strlen(siteName)); sPI += strlen(siteName);
|
|
||||||
memcpy(sPI, &n_siteCounter, sizeof(n_siteCounter)); sPI += sizeof(n_siteCounter);
|
|
||||||
if (siteContextString) {
|
|
||||||
memcpy(sPI, &n_siteContextLength, sizeof(n_siteContextLength)); sPI += sizeof(n_siteContextLength);
|
|
||||||
memcpy(sPI, siteContextString, strlen(siteContextString)); sPI += strlen(siteContextString);
|
|
||||||
}
|
|
||||||
if (sPI - sitePasswordInfo != sitePasswordInfoLength)
|
|
||||||
abort();
|
|
||||||
trc("seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)\n", mpSiteScope, Hex(&n_siteNameLength, sizeof(n_siteNameLength)), siteName, Hex(&n_siteCounter, sizeof(n_siteCounter)), Hex(&n_siteContextLength, sizeof(n_siteContextLength)), siteContextString);
|
|
||||||
trc("sitePasswordInfo ID: %s\n", IDForBuf(sitePasswordInfo, sitePasswordInfoLength));
|
|
||||||
|
|
||||||
uint8_t sitePasswordSeed[32];
|
|
||||||
HMAC_SHA256_Buf(masterKey, MP_dkLen, sitePasswordInfo, sitePasswordInfoLength, sitePasswordSeed);
|
|
||||||
memset(masterKey, 0, MP_dkLen);
|
|
||||||
memset(sitePasswordInfo, 0, sitePasswordInfoLength);
|
|
||||||
free(masterKey);
|
|
||||||
free(sitePasswordInfo);
|
|
||||||
trc("sitePasswordSeed ID: %s\n", IDForBuf(sitePasswordSeed, 32));
|
|
||||||
|
|
||||||
// Determine the cipher.
|
|
||||||
const char *cipher = CipherForType(siteType, sitePasswordSeed[0]);
|
|
||||||
trc("type %s, cipher: %s\n", siteTypeString, cipher);
|
|
||||||
if (strlen(cipher) > 32)
|
|
||||||
abort();
|
|
||||||
|
|
||||||
// Encode the password from the seed using the cipher.
|
|
||||||
char *sitePassword = (char *)calloc(strlen(cipher) + 1, sizeof(char));
|
|
||||||
for (int c = 0; c < strlen(cipher); ++c) {
|
|
||||||
sitePassword[c] = CharacterFromClass(cipher[c], sitePasswordSeed[c + 1]);
|
|
||||||
trc("class %c, character: %c\n", cipher[c], sitePassword[c]);
|
|
||||||
}
|
|
||||||
memset(sitePasswordSeed, 0, sizeof(sitePasswordSeed));
|
|
||||||
|
|
||||||
// Output the password.
|
|
||||||
fprintf( stdout, "%s\n", sitePassword );
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
1
MasterPassword/C/mpw_tests.xml
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../Java/masterpassword-algorithm/src/test/resources/mpw_tests.xml
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
//
|
|
||||||
// MPTypes.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 02/01/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <alg/sha256.h>
|
|
||||||
|
|
||||||
#include <curses.h>
|
|
||||||
#include <term.h>
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
const MPElementType TypeWithName(const char *typeName) {
|
|
||||||
char lowerTypeName[strlen(typeName)];
|
|
||||||
strcpy(lowerTypeName, typeName);
|
|
||||||
for (char *tN = lowerTypeName; *tN; ++tN)
|
|
||||||
*tN = tolower(*tN);
|
|
||||||
|
|
||||||
if (0 == strcmp(lowerTypeName, "x") || 0 == strcmp(lowerTypeName, "max") || 0 == strcmp(lowerTypeName, "maximum"))
|
|
||||||
return MPElementTypeGeneratedMaximum;
|
|
||||||
if (0 == strcmp(lowerTypeName, "l") || 0 == strcmp(lowerTypeName, "long"))
|
|
||||||
return MPElementTypeGeneratedLong;
|
|
||||||
if (0 == strcmp(lowerTypeName, "m") || 0 == strcmp(lowerTypeName, "med") || 0 == strcmp(lowerTypeName, "medium"))
|
|
||||||
return MPElementTypeGeneratedMedium;
|
|
||||||
if (0 == strcmp(lowerTypeName, "b") || 0 == strcmp(lowerTypeName, "basic"))
|
|
||||||
return MPElementTypeGeneratedBasic;
|
|
||||||
if (0 == strcmp(lowerTypeName, "s") || 0 == strcmp(lowerTypeName, "short"))
|
|
||||||
return MPElementTypeGeneratedShort;
|
|
||||||
if (0 == strcmp(lowerTypeName, "i") || 0 == strcmp(lowerTypeName, "pin"))
|
|
||||||
return MPElementTypeGeneratedPIN;
|
|
||||||
if (0 == strcmp(lowerTypeName, "n") || 0 == strcmp(lowerTypeName, "name"))
|
|
||||||
return MPElementTypeGeneratedName;
|
|
||||||
if (0 == strcmp(lowerTypeName, "p") || 0 == strcmp(lowerTypeName, "phrase"))
|
|
||||||
return MPElementTypeGeneratedPhrase;
|
|
||||||
|
|
||||||
fprintf(stderr, "Not a generated type name: %s", lowerTypeName);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *CipherForType(MPElementType type, uint8_t seedByte) {
|
|
||||||
if (!(type & MPElementTypeClassGenerated)) {
|
|
||||||
fprintf(stderr, "Not a generated type: %d", type);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case MPElementTypeGeneratedMaximum: {
|
|
||||||
const char *ciphers[] = { "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" };
|
|
||||||
return ciphers[seedByte % 2];
|
|
||||||
}
|
|
||||||
case MPElementTypeGeneratedLong: {
|
|
||||||
const char *ciphers[] = { "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno", "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno", "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno", "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno", "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno", "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno", "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" };
|
|
||||||
return ciphers[seedByte % 21];
|
|
||||||
}
|
|
||||||
case MPElementTypeGeneratedMedium: {
|
|
||||||
const char *ciphers[] = { "CvcnoCvc", "CvcCvcno" };
|
|
||||||
return ciphers[seedByte % 2];
|
|
||||||
}
|
|
||||||
case MPElementTypeGeneratedBasic: {
|
|
||||||
const char *ciphers[] = { "aaanaaan", "aannaaan", "aaannaaa" };
|
|
||||||
return ciphers[seedByte % 3];
|
|
||||||
}
|
|
||||||
case MPElementTypeGeneratedShort: {
|
|
||||||
return "Cvcn";
|
|
||||||
}
|
|
||||||
case MPElementTypeGeneratedPIN: {
|
|
||||||
return "nnnn";
|
|
||||||
}
|
|
||||||
case MPElementTypeGeneratedName: {
|
|
||||||
return "cvccvcvcv";
|
|
||||||
}
|
|
||||||
case MPElementTypeGeneratedPhrase: {
|
|
||||||
const char *ciphers[] = { "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" };
|
|
||||||
return ciphers[seedByte % 3];
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
fprintf(stderr, "Unknown generated type: %d", type);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MPElementVariant VariantWithName(const char *variantName) {
|
|
||||||
char lowerVariantName[strlen(variantName)];
|
|
||||||
strcpy(lowerVariantName, variantName);
|
|
||||||
for (char *vN = lowerVariantName; *vN; ++vN)
|
|
||||||
*vN = tolower(*vN);
|
|
||||||
|
|
||||||
if (0 == strcmp(lowerVariantName, "p") || 0 == strcmp(lowerVariantName, "password"))
|
|
||||||
return MPElementVariantPassword;
|
|
||||||
if (0 == strcmp(lowerVariantName, "l") || 0 == strcmp(lowerVariantName, "login"))
|
|
||||||
return MPElementVariantLogin;
|
|
||||||
if (0 == strcmp(lowerVariantName, "a") || 0 == strcmp(lowerVariantName, "answer"))
|
|
||||||
return MPElementVariantAnswer;
|
|
||||||
|
|
||||||
fprintf(stderr, "Not a variant name: %s", lowerVariantName);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *ScopeForVariant(MPElementVariant variant) {
|
|
||||||
switch (variant) {
|
|
||||||
case MPElementVariantPassword: {
|
|
||||||
return "com.lyndir.masterpassword";
|
|
||||||
}
|
|
||||||
case MPElementVariantLogin: {
|
|
||||||
return "com.lyndir.masterpassword.login";
|
|
||||||
}
|
|
||||||
case MPElementVariantAnswer: {
|
|
||||||
return "com.lyndir.masterpassword.answer";
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
fprintf(stderr, "Unknown variant: %d", variant);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char CharacterFromClass(char characterClass, uint8_t seedByte) {
|
|
||||||
const char *classCharacters;
|
|
||||||
switch (characterClass) {
|
|
||||||
case 'V': {
|
|
||||||
classCharacters = "AEIOU";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'C': {
|
|
||||||
classCharacters = "BCDFGHJKLMNPQRSTVWXYZ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'v': {
|
|
||||||
classCharacters = "aeiou";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'c': {
|
|
||||||
classCharacters = "bcdfghjklmnpqrstvwxyz";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'A': {
|
|
||||||
classCharacters = "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'a': {
|
|
||||||
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'n': {
|
|
||||||
classCharacters = "0123456789";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'o': {
|
|
||||||
classCharacters = "@&%?,=[]_:-+*$#!'^~;()/.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'x': {
|
|
||||||
classCharacters = "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ' ': {
|
|
||||||
classCharacters = " ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
fprintf(stderr, "Unknown character class: %c", characterClass);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return classCharacters[seedByte % strlen(classCharacters)];
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *IDForBuf(const void *buf, size_t length) {
|
|
||||||
uint8_t hash[32];
|
|
||||||
SHA256_Buf(buf, length, hash);
|
|
||||||
|
|
||||||
char *id = (char *)calloc(65, sizeof(char));
|
|
||||||
for (int kH = 0; kH < 32; kH++)
|
|
||||||
sprintf(&(id[kH * 2]), "%02X", hash[kH]);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *Hex(const void *buf, size_t length) {
|
|
||||||
char *id = (char *)calloc(length*2+1, sizeof(char));
|
|
||||||
for (int kH = 0; kH < length; kH++)
|
|
||||||
sprintf(&(id[kH * 2]), "%02X", ((const uint8_t*)buf)[kH]);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
int putvari;
|
|
||||||
char *putvarc = NULL;
|
|
||||||
static void initputvar() {
|
|
||||||
if (putvarc)
|
|
||||||
free(putvarc);
|
|
||||||
putvari=0;
|
|
||||||
putvarc=(char *)calloc(256, sizeof(char));
|
|
||||||
}
|
|
||||||
static int putvar(int c) {
|
|
||||||
putvarc[putvari++]=c;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *Identicon(const char *userName, const char *masterPassword) {
|
|
||||||
const char *left[] = { "╔", "╚", "╰", "═" };
|
|
||||||
const char *right[] = { "╗", "╝", "╯", "═" };
|
|
||||||
const char *body[] = { "█", "░", "▒", "▓", "☺", "☻" };
|
|
||||||
const char *accessory[] = { "◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣", "☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟", "♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌" };
|
|
||||||
|
|
||||||
uint8_t identiconSeed[32];
|
|
||||||
HMAC_SHA256_Buf(masterPassword, strlen(masterPassword), userName, strlen(userName), identiconSeed);
|
|
||||||
|
|
||||||
char *identicon = (char *)calloc(20, sizeof(char));
|
|
||||||
setupterm(NULL, 2, NULL);
|
|
||||||
initputvar();
|
|
||||||
tputs(tparm(tgetstr("AF", NULL), identiconSeed[4] % 7 + 1), 1, putvar);
|
|
||||||
char red[strlen(putvarc)];
|
|
||||||
strcpy(red, putvarc);
|
|
||||||
tputs(tgetstr("me", NULL), 1, putvar);
|
|
||||||
char reset[strlen(putvarc)];
|
|
||||||
strcpy(reset, putvarc);
|
|
||||||
sprintf(identicon, "%s%s%s%s%s%s",
|
|
||||||
red,
|
|
||||||
left[identiconSeed[0] % (sizeof(left) / sizeof(left[0]))],
|
|
||||||
body[identiconSeed[1] % (sizeof(body) / sizeof(body[0]))],
|
|
||||||
right[identiconSeed[2] % (sizeof(right) / sizeof(right[0]))],
|
|
||||||
accessory[identiconSeed[3] % (sizeof(accessory) / sizeof(accessory[0]))],
|
|
||||||
reset);
|
|
||||||
|
|
||||||
return identicon;
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
//
|
|
||||||
// MPTypes.h
|
|
||||||
// MasterPassword
|
|
||||||
//
|
|
||||||
// Created by Maarten Billemont on 02/01/12.
|
|
||||||
// Copyright (c) 2012 Lyndir. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
/** Generate the password to log in with. */
|
|
||||||
MPElementVariantPassword,
|
|
||||||
/** Generate the login name to log in as. */
|
|
||||||
MPElementVariantLogin,
|
|
||||||
/** Generate the answer to a security question. */
|
|
||||||
MPElementVariantAnswer,
|
|
||||||
} MPElementVariant;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
/** Generate the password. */
|
|
||||||
MPElementTypeClassGenerated = 1 << 4,
|
|
||||||
/** Store the password. */
|
|
||||||
MPElementTypeClassStored = 1 << 5,
|
|
||||||
} MPElementTypeClass;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
/** Export the key-protected content data. */
|
|
||||||
MPElementFeatureExportContent = 1 << 10,
|
|
||||||
/** Never export content. */
|
|
||||||
MPElementFeatureDevicePrivate = 1 << 11,
|
|
||||||
} MPElementFeature;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
MPElementTypeGeneratedMaximum = 0x0 | MPElementTypeClassGenerated | 0x0,
|
|
||||||
MPElementTypeGeneratedLong = 0x1 | MPElementTypeClassGenerated | 0x0,
|
|
||||||
MPElementTypeGeneratedMedium = 0x2 | MPElementTypeClassGenerated | 0x0,
|
|
||||||
MPElementTypeGeneratedBasic = 0x4 | MPElementTypeClassGenerated | 0x0,
|
|
||||||
MPElementTypeGeneratedShort = 0x3 | MPElementTypeClassGenerated | 0x0,
|
|
||||||
MPElementTypeGeneratedPIN = 0x5 | MPElementTypeClassGenerated | 0x0,
|
|
||||||
MPElementTypeGeneratedName = 0xE | MPElementTypeClassGenerated | 0x0,
|
|
||||||
MPElementTypeGeneratedPhrase = 0xF | MPElementTypeClassGenerated | 0x0,
|
|
||||||
|
|
||||||
MPElementTypeStoredPersonal = 0x0 | MPElementTypeClassStored | MPElementFeatureExportContent,
|
|
||||||
MPElementTypeStoredDevicePrivate = 0x1 | MPElementTypeClassStored | MPElementFeatureDevicePrivate,
|
|
||||||
} MPElementType;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
#define trc(...) fprintf(stderr, __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define trc(...) do {} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const MPElementVariant VariantWithName(const char *variantName);
|
|
||||||
const char *ScopeForVariant(MPElementVariant variant);
|
|
||||||
const MPElementType TypeWithName(const char *typeName);
|
|
||||||
const char *CipherForType(MPElementType type, uint8_t seedByte);
|
|
||||||
const char CharacterFromClass(char characterClass, uint8_t seedByte);
|
|
||||||
const char *IDForBuf(const void *buf, size_t length);
|
|
||||||
const char *Hex(const void *buf, size_t length);
|
|
||||||
const char *Identicon(const char *userName, const char *masterPassword);
|
|
||||||
|
|
||||||
2
MasterPassword/Java/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
target
|
||||||
|
dependency-reduced-pom.xml
|
||||||
@@ -7,13 +7,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password Algorithm Implementation</name>
|
<name>Master Password Algorithm Implementation</name>
|
||||||
<description>The implementation of the Master Password algorithm</description>
|
<description>The implementation of the Master Password algorithm</description>
|
||||||
|
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
<artifactId>masterpassword-algorithm</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
@@ -24,26 +23,33 @@
|
|||||||
<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 -->
|
||||||
<dependency>
|
|
||||||
<groupId>net.sf.plist</groupId>
|
|
||||||
<artifactId>property-list</artifactId>
|
|
||||||
<version>2.0.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lambdaworks</groupId>
|
<groupId>com.lambdaworks</groupId>
|
||||||
<artifactId>scrypt</artifactId>
|
<artifactId>scrypt</artifactId>
|
||||||
<version>1.4.0</version>
|
<version>1.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- TESTING -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.testng</groupId>
|
||||||
|
<artifactId>testng</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public enum MPElementFeature {
|
|
||||||
|
|
||||||
/** Export the key-protected content data. */
|
|
||||||
ExportContent,
|
|
||||||
/** Never export content. */
|
|
||||||
DevicePrivate,
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public enum MPElementType {
|
|
||||||
|
|
||||||
GeneratedMaximum( "Maximum Security Password", "Maximum", "20 characters, contains symbols.", MPElementTypeClass.Generated ),
|
|
||||||
GeneratedLong( "Long Password", "Long", "Copy-friendly, 14 characters, contains symbols.", MPElementTypeClass.Generated ),
|
|
||||||
GeneratedMedium( "Medium Password", "Medium", "Copy-friendly, 8 characters, contains symbols.", MPElementTypeClass.Generated ),
|
|
||||||
GeneratedBasic( "Basic Password", "Basic", "8 characters, no symbols.", MPElementTypeClass.Generated ),
|
|
||||||
GeneratedShort( "Short Password", "Short", "Copy-friendly, 4 characters, no symbols.", MPElementTypeClass.Generated ),
|
|
||||||
GeneratedPIN( "PIN", "PIN", "4 numbers.", MPElementTypeClass.Generated ),
|
|
||||||
|
|
||||||
StoredPersonal( "Personal Password", "Personal", "AES-encrypted, exportable.", MPElementTypeClass.Stored,
|
|
||||||
MPElementFeature.ExportContent ),
|
|
||||||
StoredDevicePrivate( "Device Private Password", "Private", "AES-encrypted, not exported.", MPElementTypeClass.Stored,
|
|
||||||
MPElementFeature.DevicePrivate );
|
|
||||||
|
|
||||||
static final Logger logger = Logger.get( MPElementType.class );
|
|
||||||
|
|
||||||
private final MPElementTypeClass typeClass;
|
|
||||||
private final Set<MPElementFeature> typeFeatures;
|
|
||||||
private final String name;
|
|
||||||
private final String shortName;
|
|
||||||
private final String description;
|
|
||||||
|
|
||||||
MPElementType(final String name, final String shortName, final String description, final MPElementTypeClass typeClass,
|
|
||||||
final MPElementFeature... typeFeatures) {
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
this.shortName = shortName;
|
|
||||||
this.typeClass = typeClass;
|
|
||||||
this.description = description;
|
|
||||||
|
|
||||||
ImmutableSet.Builder<MPElementFeature> typeFeaturesBuilder = ImmutableSet.builder();
|
|
||||||
for (final MPElementFeature typeFeature : typeFeatures) {
|
|
||||||
typeFeaturesBuilder.add( typeFeature );
|
|
||||||
}
|
|
||||||
this.typeFeatures = typeFeaturesBuilder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPElementTypeClass getTypeClass() {
|
|
||||||
|
|
||||||
return typeClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<MPElementFeature> getTypeFeatures() {
|
|
||||||
|
|
||||||
return typeFeatures;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getShortName() {
|
|
||||||
|
|
||||||
return shortName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param name The full or short name of the type we want to look up. It is matched case insensitively.
|
|
||||||
*
|
|
||||||
* @return The type with the given name.
|
|
||||||
*/
|
|
||||||
public static MPElementType forName(final String name) {
|
|
||||||
|
|
||||||
for (final MPElementType type : values())
|
|
||||||
if (type.getName().equalsIgnoreCase( name ) || type.getShortName().equalsIgnoreCase( name ))
|
|
||||||
return type;
|
|
||||||
|
|
||||||
throw logger.bug( "Element type not known: %s", name );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param typeClass The class for which we look up types.
|
|
||||||
*
|
|
||||||
* @return All types that support the given class.
|
|
||||||
*/
|
|
||||||
public static ImmutableList<MPElementType> forClass(final MPElementTypeClass typeClass) {
|
|
||||||
|
|
||||||
ImmutableList.Builder<MPElementType> types = ImmutableList.builder();
|
|
||||||
for (final MPElementType type : values())
|
|
||||||
if (type.getTypeClass() == typeClass)
|
|
||||||
types.add( type );
|
|
||||||
|
|
||||||
return types.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import com.lyndir.masterpassword.entity.*;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public enum MPElementTypeClass {
|
|
||||||
|
|
||||||
Generated(MPElementGeneratedEntity.class),
|
|
||||||
Stored(MPElementStoredEntity.class);
|
|
||||||
|
|
||||||
private final Class<? extends MPElementEntity> entityClass;
|
|
||||||
|
|
||||||
MPElementTypeClass(final Class<? extends MPElementEntity> entityClass) {
|
|
||||||
|
|
||||||
this.entityClass = entityClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<? extends MPElementEntity> getEntityClass() {
|
|
||||||
|
|
||||||
return entityClass;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <i>07 04, 2012</i>
|
||||||
|
*
|
||||||
|
* @author lhunath
|
||||||
|
*/
|
||||||
|
public enum MPSiteFeature {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export the key-protected content data.
|
||||||
|
*/
|
||||||
|
ExportContent( 1 << 10 ),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Never export content.
|
||||||
|
*/
|
||||||
|
DevicePrivate( 1 << 11 );
|
||||||
|
|
||||||
|
MPSiteFeature(final int mask) {
|
||||||
|
this.mask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int mask;
|
||||||
|
|
||||||
|
public int getMask() {
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <i>07 04, 2012</i>
|
||||||
|
*
|
||||||
|
* @author lhunath
|
||||||
|
*/
|
||||||
|
public enum MPSiteType {
|
||||||
|
|
||||||
|
GeneratedMaximum( "20 characters, contains symbols.", //
|
||||||
|
ImmutableList.of( "x", "max", "maximum" ), //
|
||||||
|
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
|
||||||
|
MPSiteTypeClass.Generated, 0x0 ),
|
||||||
|
|
||||||
|
GeneratedLong( "Copy-friendly, 14 characters, contains symbols.", //
|
||||||
|
ImmutableList.of( "l", "long" ), //
|
||||||
|
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
|
||||||
|
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
|
||||||
|
new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
|
||||||
|
new MPTemplate( "CvcvnoCvccCvcv" ), new MPTemplate( "CvcvCvccnoCvcv" ),
|
||||||
|
new MPTemplate( "CvcvCvccCvcvno" ), new MPTemplate( "CvcvnoCvcvCvcc" ),
|
||||||
|
new MPTemplate( "CvcvCvcvnoCvcc" ), new MPTemplate( "CvcvCvcvCvccno" ),
|
||||||
|
new MPTemplate( "CvccnoCvccCvcv" ), new MPTemplate( "CvccCvccnoCvcv" ),
|
||||||
|
new MPTemplate( "CvccCvccCvcvno" ), new MPTemplate( "CvcvnoCvccCvcc" ),
|
||||||
|
new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
|
||||||
|
new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
|
||||||
|
new MPTemplate( "CvccCvcvCvccno" ) ), //
|
||||||
|
MPSiteTypeClass.Generated, 0x1 ),
|
||||||
|
|
||||||
|
GeneratedMedium( "Copy-friendly, 8 characters, contains symbols.", //
|
||||||
|
ImmutableList.of( "m", "med", "medium" ), //
|
||||||
|
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
|
||||||
|
MPSiteTypeClass.Generated, 0x2 ),
|
||||||
|
|
||||||
|
GeneratedBasic( "8 characters, no symbols.", //
|
||||||
|
ImmutableList.of( "b", "basic" ), //
|
||||||
|
ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), //
|
||||||
|
MPSiteTypeClass.Generated, 0x3 ),
|
||||||
|
|
||||||
|
GeneratedShort( "Copy-friendly, 4 characters, no symbols.", //
|
||||||
|
ImmutableList.of( "s", "short" ), //
|
||||||
|
ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
|
||||||
|
MPSiteTypeClass.Generated, 0x4 ),
|
||||||
|
|
||||||
|
GeneratedPIN( "4 numbers.", //
|
||||||
|
ImmutableList.of( "i", "pin" ), //
|
||||||
|
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
|
||||||
|
MPSiteTypeClass.Generated, 0x5 ),
|
||||||
|
|
||||||
|
GeneratedName( "9 letter name.", //
|
||||||
|
ImmutableList.of( "n", "name" ), //
|
||||||
|
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
|
||||||
|
MPSiteTypeClass.Generated, 0xE ),
|
||||||
|
|
||||||
|
GeneratedPhrase( "20 character sentence.", //
|
||||||
|
ImmutableList.of( "p", "phrase" ), //
|
||||||
|
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
|
||||||
|
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
|
||||||
|
MPSiteTypeClass.Generated, 0xF ),
|
||||||
|
|
||||||
|
StoredPersonal( "AES-encrypted, exportable.", //
|
||||||
|
ImmutableList.of( "personal" ), //
|
||||||
|
ImmutableList.<MPTemplate>of(), //
|
||||||
|
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
|
||||||
|
|
||||||
|
StoredDevicePrivate( "AES-encrypted, not exported.", //
|
||||||
|
ImmutableList.of( "device" ), //
|
||||||
|
ImmutableList.<MPTemplate>of(), //
|
||||||
|
MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
|
||||||
|
|
||||||
|
static final Logger logger = Logger.get( MPSiteType.class );
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
private final List<String> options;
|
||||||
|
private final List<MPTemplate> templates;
|
||||||
|
private final MPSiteTypeClass typeClass;
|
||||||
|
private final int typeIndex;
|
||||||
|
private final Set<MPSiteFeature> typeFeatures;
|
||||||
|
|
||||||
|
MPSiteType(final String description, final List<String> options, final List<MPTemplate> templates, final MPSiteTypeClass typeClass,
|
||||||
|
final int typeIndex, final MPSiteFeature... typeFeatures) {
|
||||||
|
|
||||||
|
this.description = description;
|
||||||
|
this.options = options;
|
||||||
|
this.templates = templates;
|
||||||
|
this.typeClass = typeClass;
|
||||||
|
this.typeIndex = typeIndex;
|
||||||
|
|
||||||
|
ImmutableSet.Builder<MPSiteFeature> typeFeaturesBuilder = ImmutableSet.builder();
|
||||||
|
for (final MPSiteFeature typeFeature : typeFeatures) {
|
||||||
|
typeFeaturesBuilder.add( typeFeature );
|
||||||
|
}
|
||||||
|
this.typeFeatures = typeFeaturesBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MPSiteTypeClass getTypeClass() {
|
||||||
|
|
||||||
|
return typeClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<MPSiteFeature> getTypeFeatures() {
|
||||||
|
|
||||||
|
return typeFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
int mask = typeIndex | typeClass.getMask();
|
||||||
|
for (MPSiteFeature typeFeature : typeFeatures)
|
||||||
|
mask |= typeFeature.getMask();
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param option The option to select a type with. It is matched case insensitively.
|
||||||
|
*
|
||||||
|
* @return The type registered for the given option.
|
||||||
|
*/
|
||||||
|
public static MPSiteType forOption(final String option) {
|
||||||
|
|
||||||
|
for (final MPSiteType type : values())
|
||||||
|
if (type.getOptions().contains( option.toLowerCase() ))
|
||||||
|
return type;
|
||||||
|
|
||||||
|
throw logger.bug( "No type for option: %s", option );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name The name fromInt the type to look up. It is matched case insensitively.
|
||||||
|
*
|
||||||
|
* @return The type registered with the given name.
|
||||||
|
*/
|
||||||
|
public static MPSiteType forName(@Nullable final String name) {
|
||||||
|
|
||||||
|
if (name == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for (final MPSiteType type : values())
|
||||||
|
if (type.name().equalsIgnoreCase( name ))
|
||||||
|
return type;
|
||||||
|
|
||||||
|
throw logger.bug( "No type for name: %s", name );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param typeClass The class for which we look up types.
|
||||||
|
*
|
||||||
|
* @return All types that support the given class.
|
||||||
|
*/
|
||||||
|
public static ImmutableList<MPSiteType> forClass(final MPSiteTypeClass typeClass) {
|
||||||
|
|
||||||
|
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
||||||
|
for (final MPSiteType type : values())
|
||||||
|
if (type.getTypeClass() == typeClass)
|
||||||
|
types.add( type );
|
||||||
|
|
||||||
|
return types.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type The type for which we look up types.
|
||||||
|
*
|
||||||
|
* @return The type registered with the given type.
|
||||||
|
*/
|
||||||
|
public static MPSiteType forType(final int type) {
|
||||||
|
|
||||||
|
for (MPSiteType siteType : values())
|
||||||
|
if (siteType.getType() == type)
|
||||||
|
return siteType;
|
||||||
|
|
||||||
|
throw logger.bug( "No type: %s", type );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mask The mask for which we look up types.
|
||||||
|
*
|
||||||
|
* @return All types that support the given mask.
|
||||||
|
*/
|
||||||
|
public static ImmutableList<MPSiteType> forMask(final int mask) {
|
||||||
|
|
||||||
|
int typeMask = mask & ~0xF;
|
||||||
|
ImmutableList.Builder<MPSiteType> types = ImmutableList.builder();
|
||||||
|
for (MPSiteType siteType : values())
|
||||||
|
if (((siteType.getType() & ~0xF) & typeMask) != 0)
|
||||||
|
types.add( siteType );
|
||||||
|
|
||||||
|
return types.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
|
||||||
|
return templates.get( templateIndex % templates.size() );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <i>07 04, 2012</i>
|
||||||
|
*
|
||||||
|
* @author lhunath
|
||||||
|
*/
|
||||||
|
public enum MPSiteTypeClass {
|
||||||
|
Generated( 1 << 4 ),
|
||||||
|
Stored( 1 << 5 );
|
||||||
|
|
||||||
|
private final int mask;
|
||||||
|
|
||||||
|
MPSiteTypeClass(final int mask) {
|
||||||
|
this.mask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMask() {
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 14-12-02
|
||||||
|
*/
|
||||||
|
public enum MPSiteVariant {
|
||||||
|
Password( "The password to log in with.", "Doesn't currently use a context.", //
|
||||||
|
ImmutableList.of( "p", "password" ), "com.lyndir.masterpassword" ),
|
||||||
|
Login( "The username to log in as.", "Doesn't currently use a context.", //
|
||||||
|
ImmutableList.of( "l", "login" ), "com.lyndir.masterpassword.login" ),
|
||||||
|
Answer( "The answer to a security question.", "Empty for a universal site answer or\nthe most significant word(s) of the question.", //
|
||||||
|
ImmutableList.of( "a", "answer" ), "com.lyndir.masterpassword.answer" );
|
||||||
|
|
||||||
|
static final Logger logger = Logger.get( MPSiteType.class );
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
private final String contextDescription;
|
||||||
|
private final List<String> options;
|
||||||
|
private final String scope;
|
||||||
|
|
||||||
|
MPSiteVariant(final String description, final String contextDescription, final List<String> options, final String scope) {
|
||||||
|
this.contextDescription = contextDescription;
|
||||||
|
|
||||||
|
this.options = options;
|
||||||
|
this.description = description;
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContextDescription() {
|
||||||
|
return contextDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getOptions() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param option The option to select a variant with. It is matched case insensitively.
|
||||||
|
*
|
||||||
|
* @return The variant registered for the given option.
|
||||||
|
*/
|
||||||
|
public static MPSiteVariant forOption(final String option) {
|
||||||
|
|
||||||
|
for (final MPSiteVariant variant : values())
|
||||||
|
if (variant.getOptions().contains( option.toLowerCase() ))
|
||||||
|
return variant;
|
||||||
|
|
||||||
|
throw logger.bug( "No variant for option: %s", option );
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param name The name fromInt the variant to look up. It is matched case insensitively.
|
||||||
|
*
|
||||||
|
* @return The variant registered with the given name.
|
||||||
|
*/
|
||||||
|
public static MPSiteVariant forName(@Nullable final String name) {
|
||||||
|
|
||||||
|
if (name == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for (final MPSiteVariant type : values())
|
||||||
|
if (type.name().equalsIgnoreCase( name ))
|
||||||
|
return type;
|
||||||
|
|
||||||
|
throw logger.bug( "No variant for name: %s", name );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,20 +14,21 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class MPTemplate extends MetaObject {
|
public class MPTemplate extends MetaObject {
|
||||||
|
|
||||||
|
private final String templateString;
|
||||||
private final List<MPTemplateCharacterClass> template;
|
private final List<MPTemplateCharacterClass> template;
|
||||||
|
|
||||||
public MPTemplate(final String template, final Map<Character, MPTemplateCharacterClass> characterClasses) {
|
MPTemplate(final String templateString) {
|
||||||
|
|
||||||
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.<MPTemplateCharacterClass>builder();
|
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.builder();
|
||||||
for (int i = 0; i < template.length(); ++i)
|
for (int i = 0; i < templateString.length(); ++i)
|
||||||
builder.add( characterClasses.get( template.charAt( i ) ) );
|
builder.add( MPTemplateCharacterClass.forIdentifier( templateString.charAt( i ) ) );
|
||||||
|
|
||||||
this.template = builder.build();
|
this.templateString = templateString;
|
||||||
|
template = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPTemplate(final List<MPTemplateCharacterClass> template) {
|
public String getTemplateString() {
|
||||||
|
return templateString;
|
||||||
this.template = template;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {
|
public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {
|
||||||
@@ -38,4 +40,9 @@ public class MPTemplate extends MetaObject {
|
|||||||
|
|
||||||
return template.size();
|
return template.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return strf( "{MPTemplate: %s}", templateString );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.lhunath.opal.system.util.ObjectMeta;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,16 +8,29 @@ import com.lyndir.lhunath.opal.system.util.ObjectMeta;
|
|||||||
*
|
*
|
||||||
* @author lhunath
|
* @author lhunath
|
||||||
*/
|
*/
|
||||||
public class MPTemplateCharacterClass extends MetaObject {
|
public enum MPTemplateCharacterClass {
|
||||||
|
|
||||||
|
UpperVowel( 'V', "AEIOU" ),
|
||||||
|
UpperConsonant( 'C', "BCDFGHJKLMNPQRSTVWXYZ" ),
|
||||||
|
LowerVowel( 'v', "aeiou" ),
|
||||||
|
LowerConsonant( 'c', "bcdfghjklmnpqrstvwxyz" ),
|
||||||
|
UpperAlphanumeric( 'A', "AEIOUBCDFGHJKLMNPQRSTVWXYZ" ),
|
||||||
|
Alphanumeric( 'a', "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz" ),
|
||||||
|
Numeric( 'n', "0123456789" ),
|
||||||
|
Other( 'o', "@&%?,=[]_:-+*$#!'^~;()/." ),
|
||||||
|
Any( 'x', "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()" ),
|
||||||
|
Space( ' ', " " );
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MPTemplateCharacterClass.class );
|
||||||
|
|
||||||
private final char identifier;
|
private final char identifier;
|
||||||
@ObjectMeta(useFor = { })
|
|
||||||
private final char[] characters;
|
private final char[] characters;
|
||||||
|
|
||||||
public MPTemplateCharacterClass(final char identifier, final char[] characters) {
|
MPTemplateCharacterClass(final char identifier, final String characters) {
|
||||||
|
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
this.characters = characters;
|
this.characters = characters.toCharArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getIdentifier() {
|
public char getIdentifier() {
|
||||||
@@ -30,4 +42,12 @@ public class MPTemplateCharacterClass extends MetaObject {
|
|||||||
|
|
||||||
return characters[index % characters.length];
|
return characters[index % characters.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MPTemplateCharacterClass forIdentifier(final char identifier) {
|
||||||
|
for (MPTemplateCharacterClass characterClass : values())
|
||||||
|
if (characterClass.getIdentifier() == identifier)
|
||||||
|
return characterClass;
|
||||||
|
|
||||||
|
throw logger.bug( "No character class defined for identifier: %s", identifier );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
package com.lyndir.masterpassword;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.io.Closeables;
|
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
|
||||||
import com.lyndir.lhunath.opal.system.util.MetaObject;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import net.sf.plist.*;
|
|
||||||
import net.sf.plist.io.PropertyListException;
|
|
||||||
import net.sf.plist.io.PropertyListParser;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public class MPTemplates extends MetaObject {
|
|
||||||
|
|
||||||
static final Logger logger = Logger.get( MPTemplates.class );
|
|
||||||
|
|
||||||
private final Map<MPElementType, List<MPTemplate>> templates;
|
|
||||||
|
|
||||||
public MPTemplates(final Map<MPElementType, List<MPTemplate>> templates) {
|
|
||||||
|
|
||||||
this.templates = templates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MPTemplates load() {
|
|
||||||
|
|
||||||
return loadFromPList( "ciphers.plist" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MPTemplates loadFromPList(final String templateResource) {
|
|
||||||
|
|
||||||
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
|
|
||||||
InputStream templateStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( templateResource );
|
|
||||||
Preconditions.checkNotNull( templateStream, "Not found: %s", templateResource );
|
|
||||||
try {
|
|
||||||
NSObject plistObject = PropertyListParser.parse( templateStream );
|
|
||||||
Preconditions.checkState( NSDictionary.class.isAssignableFrom( plistObject.getClass() ) );
|
|
||||||
NSDictionary plist = (NSDictionary) plistObject;
|
|
||||||
|
|
||||||
NSDictionary characterClassesDict = (NSDictionary) plist.get( "MPCharacterClasses" );
|
|
||||||
NSDictionary templatesDict = (NSDictionary) plist.get( "MPElementGeneratedEntity" );
|
|
||||||
|
|
||||||
ImmutableMap.Builder<Character, MPTemplateCharacterClass> characterClassesBuilder = ImmutableMap.builder();
|
|
||||||
for (final Map.Entry<String, NSObject> characterClassEntry : characterClassesDict.entrySet()) {
|
|
||||||
String key = characterClassEntry.getKey();
|
|
||||||
NSObject value = characterClassEntry.getValue();
|
|
||||||
Preconditions.checkState( key.length() == 1 );
|
|
||||||
Preconditions.checkState( NSString.class.isAssignableFrom( value.getClass() ));
|
|
||||||
|
|
||||||
char character = key.charAt( 0 );
|
|
||||||
char[] characterClass = ((NSString)value).getValue().toCharArray();
|
|
||||||
characterClassesBuilder.put( character, new MPTemplateCharacterClass( character, characterClass ) );
|
|
||||||
}
|
|
||||||
ImmutableMap<Character, MPTemplateCharacterClass> characterClasses = characterClassesBuilder.build();
|
|
||||||
|
|
||||||
ImmutableMap.Builder<MPElementType, List<MPTemplate>> templatesBuilder = ImmutableMap.builder();
|
|
||||||
for (final Map.Entry<String, NSObject> template : templatesDict.entrySet()) {
|
|
||||||
String key = template.getKey();
|
|
||||||
NSObject value = template.getValue();
|
|
||||||
Preconditions.checkState( NSArray.class.isAssignableFrom( value.getClass() ) );
|
|
||||||
|
|
||||||
MPElementType type = MPElementType.forName( key );
|
|
||||||
List<NSObject> templateStrings = ((NSArray) value).getValue();
|
|
||||||
|
|
||||||
ImmutableList.Builder<MPTemplate> typeTemplatesBuilder = ImmutableList.<MPTemplate>builder();
|
|
||||||
for (final NSObject templateString : templateStrings)
|
|
||||||
typeTemplatesBuilder.add( new MPTemplate( ((NSString) templateString).getValue(), characterClasses ) );
|
|
||||||
|
|
||||||
templatesBuilder.put( type, typeTemplatesBuilder.build() );
|
|
||||||
}
|
|
||||||
ImmutableMap<MPElementType, List<MPTemplate>> templates = templatesBuilder.build();
|
|
||||||
|
|
||||||
return new MPTemplates( templates );
|
|
||||||
}
|
|
||||||
catch (PropertyListException e) {
|
|
||||||
logger.err( e, "Could not parse templates from: %s", templateResource );
|
|
||||||
throw Throwables.propagate( e );
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
logger.err( e, "Could not read templates from: %s", templateResource );
|
|
||||||
throw Throwables.propagate( e );
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
Closeables.closeQuietly( templateStream );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPTemplate getTemplateForTypeAtRollingIndex(final MPElementType type, final int templateIndex) {
|
|
||||||
|
|
||||||
List<MPTemplate> typeTemplates = templates.get( type );
|
|
||||||
|
|
||||||
return typeTemplates.get( templateIndex % typeTemplates.size() );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(final String... arguments) {
|
|
||||||
|
|
||||||
load();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +1,164 @@
|
|||||||
package com.lyndir.masterpassword;
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.io.CharSource;
|
|
||||||
import com.google.common.io.CharStreams;
|
|
||||||
import com.google.common.primitives.Bytes;
|
|
||||||
import com.lambdaworks.crypto.SCrypt;
|
|
||||||
import com.lyndir.lhunath.opal.crypto.CryptUtils;
|
|
||||||
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.xml.stream.events.Characters;
|
import javax.annotation.Nonnull;
|
||||||
|
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 {
|
||||||
|
|
||||||
@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 Charset MP_charset = Charsets.UTF_8;
|
|
||||||
private static final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
|
|
||||||
private static final MessageDigests MP_hash = MessageDigests.SHA256;
|
|
||||||
private static final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
|
|
||||||
private static final MPTemplates templates = MPTemplates.load();
|
|
||||||
|
|
||||||
private final String userName;
|
@Nonnull
|
||||||
private final byte[] key;
|
private final String fullName;
|
||||||
|
|
||||||
private boolean valid;
|
@Nullable
|
||||||
|
private byte[] masterKey;
|
||||||
|
|
||||||
public MasterKey(final String userName, final String masterPassword) {
|
public static MasterKey create(final String fullName, final char[] masterPassword) {
|
||||||
|
|
||||||
this.userName = userName;
|
return create( Version.CURRENT, fullName, masterPassword );
|
||||||
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
byte[] userNameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE )
|
|
||||||
.order( MP_byteOrder )
|
|
||||||
.putInt( userName.length() )
|
|
||||||
.array();
|
|
||||||
byte[] salt = Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
|
|
||||||
userNameLengthBytes, userName.getBytes( MP_charset ) );
|
|
||||||
|
|
||||||
try {
|
|
||||||
key = SCrypt.scrypt( masterPassword.getBytes( MP_charset ), salt, MP_N, MP_r, MP_p, MP_dkLen );
|
|
||||||
valid = true;
|
|
||||||
|
|
||||||
logger.trc( "User: %s, master password derives to key ID: %s (took %.2fs)", //
|
|
||||||
userName, getKeyID(), (double) (System.currentTimeMillis() - start) / 1000 );
|
|
||||||
}
|
|
||||||
catch (GeneralSecurityException e) {
|
|
||||||
throw logger.bug( e );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUserName() {
|
@Nonnull
|
||||||
|
public static MasterKey create(Version version, final String fullName, final char[] masterPassword) {
|
||||||
|
|
||||||
return userName;
|
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 );
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKeyID() {
|
throw new UnsupportedOperationException( "Unsupported version: " + version );
|
||||||
|
|
||||||
Preconditions.checkState( valid );
|
|
||||||
return CodeUtils.encodeHex( MP_hash.of( key ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getSubkey(final int subkeyLength) {
|
protected MasterKey(@NotNull final String fullName) {
|
||||||
|
|
||||||
Preconditions.checkState( valid );
|
this.fullName = fullName;
|
||||||
byte[] subkey = new byte[Math.min( subkeyLength, key.length )];
|
logger.trc( "fullName: %s", fullName );
|
||||||
System.arraycopy( key, 0, subkey, 0, subkey.length );
|
|
||||||
|
|
||||||
return subkey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String encode(final String name, final MPElementType type, int counter) {
|
@Nullable
|
||||||
|
protected abstract byte[] deriveKey(final char[] masterPassword);
|
||||||
|
|
||||||
Preconditions.checkState( valid );
|
public abstract Version getAlgorithmVersion();
|
||||||
Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated );
|
|
||||||
Preconditions.checkArgument( !name.isEmpty() );
|
|
||||||
|
|
||||||
if (counter == 0)
|
@NotNull
|
||||||
counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
public String getFullName() {
|
||||||
|
|
||||||
byte[] nameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MP_byteOrder ).putInt( name.length() ).array();
|
return fullName;
|
||||||
byte[] counterBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MP_byteOrder ).putInt( counter ).array();
|
|
||||||
logger.trc( "seed from: hmac-sha256(%s, 'com.lyndir.masterpassword' | %s | %s | %s)", CryptUtils.encodeBase64( key ),
|
|
||||||
CodeUtils.encodeHex( nameLengthBytes ), name, CodeUtils.encodeHex( counterBytes ) );
|
|
||||||
byte[] seed = MP_mac.of( key, Bytes.concat( "com.lyndir.masterpassword".getBytes( MP_charset ), //
|
|
||||||
nameLengthBytes, //
|
|
||||||
name.getBytes( MP_charset ), //
|
|
||||||
counterBytes ) );
|
|
||||||
logger.trc( "seed is: %s", CryptUtils.encodeBase64( seed ) );
|
|
||||||
|
|
||||||
Preconditions.checkState( seed.length > 0 );
|
|
||||||
int templateIndex = seed[0] & 0xFF; // Mask the integer's sign.
|
|
||||||
MPTemplate template = templates.getTemplateForTypeAtRollingIndex( type, templateIndex );
|
|
||||||
logger.trc( "type: %s, template: %s", type, template );
|
|
||||||
|
|
||||||
StringBuilder password = new StringBuilder( template.length() );
|
|
||||||
for (int i = 0; i < template.length(); ++i) {
|
|
||||||
int characterIndex = seed[i + 1] & 0xFF; // Mask the integer's sign.
|
|
||||||
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
|
||||||
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
|
||||||
logger.trc( "class: %s, index: %d, byte: 0x%02X, chosen password character: %s", characterClass, characterIndex, seed[i + 1],
|
|
||||||
passwordCharacter );
|
|
||||||
|
|
||||||
password.append( passwordCharacter );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return password.toString();
|
@Nonnull
|
||||||
|
protected byte[] getKey() {
|
||||||
|
|
||||||
|
return Preconditions.checkNotNull( masterKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getKeyID() {
|
||||||
|
|
||||||
|
return idForBytes( getKey() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String encode(@Nonnull final String siteName, final MPSiteType siteType, int siteCounter,
|
||||||
|
final MPSiteVariant siteVariant, @Nullable final String siteContext);
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
return masterKey != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
|
|
||||||
valid = false;
|
if (masterKey != null) {
|
||||||
Arrays.fill( key, (byte) 0 );
|
Arrays.fill( masterKey, (byte) 0 );
|
||||||
|
masterKey = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterKey revalidate(final char[] masterPassword) {
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
logger.trc( "masterPassword: %s", new String( masterPassword ) );
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
masterKey = deriveKey( masterPassword );
|
||||||
|
|
||||||
|
if (masterKey == null)
|
||||||
|
logger.dbg( "masterKey calculation failed after %.2fs.", (System.currentTimeMillis() - start) / 1000D );
|
||||||
|
else
|
||||||
|
logger.trc( "masterKey ID: %s (derived in %.2fs)", CodeUtils.encodeHex( idForBytes( masterKey ) ),
|
||||||
|
(System.currentTimeMillis() - start) / 1000D );
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract byte[] bytesForInt(final int integer);
|
||||||
|
|
||||||
|
protected abstract byte[] idForBytes(final byte[] bytes);
|
||||||
|
|
||||||
|
public enum Version {
|
||||||
|
/**
|
||||||
|
* bugs:
|
||||||
|
* - does math with chars whose signedness was platform-dependent.
|
||||||
|
* - miscounted the byte-length fromInt multi-byte site names.
|
||||||
|
* - miscounted the byte-length fromInt multi-byte full names.
|
||||||
|
*/
|
||||||
|
V0,
|
||||||
|
/**
|
||||||
|
* bugs:
|
||||||
|
* - miscounted the byte-length fromInt multi-byte site names.
|
||||||
|
* - miscounted the byte-length fromInt multi-byte full names.
|
||||||
|
*/
|
||||||
|
V1,
|
||||||
|
/**
|
||||||
|
* bugs:
|
||||||
|
* - miscounted the byte-length fromInt multi-byte full names.
|
||||||
|
*/
|
||||||
|
V2,
|
||||||
|
/**
|
||||||
|
* bugs:
|
||||||
|
* - no known issues.
|
||||||
|
*/
|
||||||
|
V3;
|
||||||
|
|
||||||
|
public static final Version CURRENT = V3;
|
||||||
|
|
||||||
|
public static Version fromInt(final int algorithmVersion) {
|
||||||
|
|
||||||
|
return values()[algorithmVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int toInt() {
|
||||||
|
|
||||||
|
return ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toBundleVersion() {
|
||||||
|
switch (this) {
|
||||||
|
case V0:
|
||||||
|
return "1.0";
|
||||||
|
case V1:
|
||||||
|
return "2.0";
|
||||||
|
case V2:
|
||||||
|
return "2.1";
|
||||||
|
case V3:
|
||||||
|
return "2.2";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException( "Unsupported version: " + this );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
import com.lambdaworks.crypto.SCrypt;
|
||||||
|
import com.lyndir.lhunath.opal.system.*;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.nio.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bugs:
|
||||||
|
* - does math with chars whose signedness was platform-dependent.
|
||||||
|
* - miscounted the byte-length fromInt multi-byte site names.
|
||||||
|
* - miscounted the byte-length fromInt multi-byte full names.
|
||||||
|
*
|
||||||
|
* @author lhunath, 2014-08-30
|
||||||
|
*/
|
||||||
|
public class MasterKeyV0 extends MasterKey {
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MasterKeyV0.class );
|
||||||
|
|
||||||
|
protected final int MP_N = 32768;
|
||||||
|
protected final int MP_r = 8;
|
||||||
|
protected final int MP_p = 2;
|
||||||
|
protected final int MP_dkLen = 64;
|
||||||
|
protected final int MP_intLen = 32;
|
||||||
|
protected final Charset MP_charset = Charsets.UTF_8;
|
||||||
|
protected final ByteOrder MP_byteOrder = ByteOrder.BIG_ENDIAN;
|
||||||
|
protected final MessageDigests MP_hash = MessageDigests.SHA256;
|
||||||
|
protected final MessageAuthenticationDigests MP_mac = MessageAuthenticationDigests.HmacSHA256;
|
||||||
|
|
||||||
|
public MasterKeyV0(final String fullName) {
|
||||||
|
super( fullName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getAlgorithmVersion() {
|
||||||
|
|
||||||
|
return Version.V0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected byte[] deriveKey(final char[] masterPassword) {
|
||||||
|
String fullName = getFullName();
|
||||||
|
byte[] fullNameBytes = fullName.getBytes( MP_charset );
|
||||||
|
byte[] fullNameLengthBytes = bytesForInt( fullName.length() );
|
||||||
|
|
||||||
|
String mpKeyScope = MPSiteVariant.Password.getScope();
|
||||||
|
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
|
||||||
|
logger.trc( "key scope: %s", mpKeyScope );
|
||||||
|
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||||
|
|
||||||
|
CharBuffer mpChars = CharBuffer.wrap( masterPassword );
|
||||||
|
byte[] mpBytes = MP_charset.encode( mpChars ).array();
|
||||||
|
try {
|
||||||
|
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||||
|
}
|
||||||
|
catch (GeneralSecurityException e) {
|
||||||
|
logger.bug( e );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Arrays.fill( mpBytes, (byte) 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
|
||||||
|
@Nullable final String siteContext) {
|
||||||
|
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||||
|
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||||
|
|
||||||
|
logger.trc( "siteName: %s", siteName );
|
||||||
|
logger.trc( "siteCounter: %d", siteCounter );
|
||||||
|
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||||
|
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||||
|
|
||||||
|
if (siteCounter == 0)
|
||||||
|
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
||||||
|
|
||||||
|
String siteScope = siteVariant.getScope();
|
||||||
|
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
||||||
|
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
|
||||||
|
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||||
|
byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
|
||||||
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
|
||||||
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
|
siteContext == null? "(null)": siteContext );
|
||||||
|
|
||||||
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
|
if (siteContextBytes != null)
|
||||||
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||||
|
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
|
byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
|
||||||
|
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
||||||
|
|
||||||
|
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||||
|
int templateIndex = sitePasswordSeed[0] & 0xFFFF;
|
||||||
|
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||||
|
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||||
|
|
||||||
|
StringBuilder password = new StringBuilder( template.length() );
|
||||||
|
for (int i = 0; i < template.length(); ++i) {
|
||||||
|
int characterIndex = sitePasswordSeed[i + 1] & 0xFFFF;
|
||||||
|
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||||
|
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||||
|
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
||||||
|
sitePasswordSeed[i + 1], passwordCharacter );
|
||||||
|
|
||||||
|
password.append( passwordCharacter );
|
||||||
|
}
|
||||||
|
|
||||||
|
return password.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] bytesForInt(final int integer) {
|
||||||
|
return ByteBuffer.allocate( MP_intLen / Byte.SIZE ).order( MP_byteOrder ).putInt( integer ).array();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] idForBytes(final byte[] bytes) {
|
||||||
|
return MP_hash.of( bytes );
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
import com.lyndir.lhunath.opal.system.*;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bugs:
|
||||||
|
* - miscounted the byte-length fromInt multi-byte site names.
|
||||||
|
* - miscounted the byte-length fromInt multi-byte full names.
|
||||||
|
*
|
||||||
|
* @author lhunath, 2014-08-30
|
||||||
|
*/
|
||||||
|
public class MasterKeyV1 extends MasterKeyV0 {
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MasterKeyV1.class );
|
||||||
|
|
||||||
|
public MasterKeyV1(final String fullName) {
|
||||||
|
super( fullName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getAlgorithmVersion() {
|
||||||
|
|
||||||
|
return Version.V1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
|
||||||
|
@Nullable final String siteContext) {
|
||||||
|
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||||
|
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||||
|
|
||||||
|
logger.trc( "siteName: %s", siteName );
|
||||||
|
logger.trc( "siteCounter: %d", siteCounter );
|
||||||
|
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||||
|
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||||
|
|
||||||
|
if (siteCounter == 0)
|
||||||
|
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
||||||
|
|
||||||
|
String siteScope = siteVariant.getScope();
|
||||||
|
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
||||||
|
byte[] siteNameLengthBytes = bytesForInt( siteName.length() );
|
||||||
|
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||||
|
byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
|
||||||
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
|
||||||
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
|
siteContext == null? "(null)": siteContext );
|
||||||
|
|
||||||
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
|
if (siteContextBytes != null)
|
||||||
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||||
|
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
|
byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
|
||||||
|
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
||||||
|
|
||||||
|
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||||
|
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
|
||||||
|
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||||
|
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||||
|
|
||||||
|
StringBuilder password = new StringBuilder( template.length() );
|
||||||
|
for (int i = 0; i < template.length(); ++i) {
|
||||||
|
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
|
||||||
|
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||||
|
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||||
|
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
||||||
|
sitePasswordSeed[i + 1], passwordCharacter );
|
||||||
|
|
||||||
|
password.append( passwordCharacter );
|
||||||
|
}
|
||||||
|
|
||||||
|
return password.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bugs:
|
||||||
|
* - miscounted the byte-length fromInt multi-byte full names.
|
||||||
|
*
|
||||||
|
* @author lhunath, 2014-08-30
|
||||||
|
*/
|
||||||
|
public class MasterKeyV2 extends MasterKeyV1 {
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MasterKeyV2.class );
|
||||||
|
|
||||||
|
public MasterKeyV2(final String fullName) {
|
||||||
|
super( fullName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getAlgorithmVersion() {
|
||||||
|
|
||||||
|
return Version.V2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
|
||||||
|
@Nullable final String siteContext) {
|
||||||
|
Preconditions.checkArgument( siteType.getTypeClass() == MPSiteTypeClass.Generated );
|
||||||
|
Preconditions.checkArgument( !siteName.isEmpty() );
|
||||||
|
|
||||||
|
logger.trc( "siteName: %s", siteName );
|
||||||
|
logger.trc( "siteCounter: %d", siteCounter );
|
||||||
|
logger.trc( "siteVariant: %d (%s)", siteVariant.ordinal(), siteVariant );
|
||||||
|
logger.trc( "siteType: %d (%s)", siteType.ordinal(), siteType );
|
||||||
|
|
||||||
|
if (siteCounter == 0)
|
||||||
|
siteCounter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
|
||||||
|
|
||||||
|
String siteScope = siteVariant.getScope();
|
||||||
|
byte[] siteNameBytes = siteName.getBytes( MP_charset );
|
||||||
|
byte[] siteNameLengthBytes = bytesForInt( siteNameBytes.length );
|
||||||
|
byte[] siteCounterBytes = bytesForInt( siteCounter );
|
||||||
|
byte[] siteContextBytes = siteContext == null? null: siteContext.getBytes( MP_charset );
|
||||||
|
byte[] siteContextLengthBytes = bytesForInt( siteContextBytes == null? 0: siteContextBytes.length );
|
||||||
|
logger.trc( "site scope: %s, context: %s", siteScope, siteContext == null? "<empty>": siteContext );
|
||||||
|
logger.trc( "seed from: hmac-sha256(masterKey, %s | %s | %s | %s | %s | %s)", siteScope, CodeUtils.encodeHex( siteNameLengthBytes ),
|
||||||
|
siteName, CodeUtils.encodeHex( siteCounterBytes ), CodeUtils.encodeHex( siteContextLengthBytes ),
|
||||||
|
siteContext == null? "(null)": siteContext );
|
||||||
|
|
||||||
|
byte[] sitePasswordInfo = Bytes.concat( siteScope.getBytes( MP_charset ), siteNameLengthBytes, siteNameBytes, siteCounterBytes );
|
||||||
|
if (siteContextBytes != null)
|
||||||
|
sitePasswordInfo = Bytes.concat( sitePasswordInfo, siteContextLengthBytes, siteContextBytes );
|
||||||
|
logger.trc( "sitePasswordInfo ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordInfo ) ) );
|
||||||
|
|
||||||
|
byte[] sitePasswordSeed = MP_mac.of( getKey(), sitePasswordInfo );
|
||||||
|
logger.trc( "sitePasswordSeed ID: %s", CodeUtils.encodeHex( idForBytes( sitePasswordSeed ) ) );
|
||||||
|
|
||||||
|
Preconditions.checkState( sitePasswordSeed.length > 0 );
|
||||||
|
int templateIndex = sitePasswordSeed[0] & 0xFF; // Mask the integer's sign.
|
||||||
|
MPTemplate template = siteType.getTemplateAtRollingIndex( templateIndex );
|
||||||
|
logger.trc( "type %s, template: %s", siteType, template.getTemplateString() );
|
||||||
|
|
||||||
|
StringBuilder password = new StringBuilder( template.length() );
|
||||||
|
for (int i = 0; i < template.length(); ++i) {
|
||||||
|
int characterIndex = sitePasswordSeed[i + 1] & 0xFF; // Mask the integer's sign.
|
||||||
|
MPTemplateCharacterClass characterClass = template.getCharacterClassAtIndex( i );
|
||||||
|
char passwordCharacter = characterClass.getCharacterAtRollingIndex( characterIndex );
|
||||||
|
logger.trc( "class %c, index %d (0x%02X) -> character: %c", characterClass.getIdentifier(), characterIndex,
|
||||||
|
sitePasswordSeed[i + 1], passwordCharacter );
|
||||||
|
|
||||||
|
password.append( passwordCharacter );
|
||||||
|
}
|
||||||
|
|
||||||
|
return password.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
import com.lambdaworks.crypto.SCrypt;
|
||||||
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bugs:
|
||||||
|
* - no known issues.
|
||||||
|
*
|
||||||
|
* @author lhunath, 2014-08-30
|
||||||
|
*/
|
||||||
|
public class MasterKeyV3 extends MasterKeyV2 {
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MasterKeyV3.class );
|
||||||
|
|
||||||
|
public MasterKeyV3(final String fullName) {
|
||||||
|
super( fullName );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getAlgorithmVersion() {
|
||||||
|
|
||||||
|
return Version.V3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected byte[] deriveKey(final char[] masterPassword) {
|
||||||
|
byte[] fullNameBytes = getFullName().getBytes( MP_charset );
|
||||||
|
byte[] fullNameLengthBytes = bytesForInt( fullNameBytes.length );
|
||||||
|
|
||||||
|
String mpKeyScope = MPSiteVariant.Password.getScope();
|
||||||
|
byte[] masterKeySalt = Bytes.concat( mpKeyScope.getBytes( MP_charset ), fullNameLengthBytes, fullNameBytes );
|
||||||
|
logger.trc( "key scope: %s", mpKeyScope );
|
||||||
|
logger.trc( "masterKeySalt ID: %s", CodeUtils.encodeHex( idForBytes( masterKeySalt ) ) );
|
||||||
|
|
||||||
|
CharBuffer mpChars = CharBuffer.wrap( masterPassword );
|
||||||
|
byte[] mpBytes = MP_charset.encode( mpChars ).array();
|
||||||
|
try {
|
||||||
|
return SCrypt.scrypt( mpBytes, masterKeySalt, MP_N, MP_r, MP_p, MP_dkLen );
|
||||||
|
}
|
||||||
|
catch (GeneralSecurityException e) {
|
||||||
|
logger.bug( e );
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Arrays.fill( mpBytes, (byte) 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.entity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public class MPElementEntity {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.entity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public class MPElementGeneratedEntity extends MPElementEntity {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.lyndir.masterpassword.entity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <i>07 04, 2012</i>
|
|
||||||
*
|
|
||||||
* @author lhunath
|
|
||||||
*/
|
|
||||||
public class MPElementStoredEntity extends MPElementEntity {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author lhunath, 15-02-04
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@ParametersAreNonnullByDefault package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../../../MasterPassword/Resources/Data/ciphers.plist
|
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
|
||||||
|
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import com.lyndir.lhunath.opal.system.util.NNSupplier;
|
||||||
|
import com.lyndir.lhunath.opal.system.util.NSupplier;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.xml.bind.annotation.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 14-12-05
|
||||||
|
*/
|
||||||
|
@XmlRootElement(name = "tests")
|
||||||
|
public class MPWTests {
|
||||||
|
|
||||||
|
public static final String ID_DEFAULT = "default";
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MPWTests.class );
|
||||||
|
|
||||||
|
@XmlElement(name = "case")
|
||||||
|
private List<Case> cases;
|
||||||
|
|
||||||
|
public List<Case> getCases() {
|
||||||
|
return cases;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Case getCase(String identifier) {
|
||||||
|
for (Case testCase : getCases())
|
||||||
|
if (identifier.equals( testCase.getIdentifier() ))
|
||||||
|
return testCase;
|
||||||
|
|
||||||
|
throw new IllegalArgumentException( "No case for identifier: " + identifier );
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlRootElement(name = "case")
|
||||||
|
public static class Case {
|
||||||
|
|
||||||
|
@XmlAttribute(name = "id")
|
||||||
|
private String identifier;
|
||||||
|
@XmlAttribute
|
||||||
|
private String parent;
|
||||||
|
@XmlElement
|
||||||
|
private String fullName;
|
||||||
|
@XmlElement
|
||||||
|
private String masterPassword;
|
||||||
|
@XmlElement
|
||||||
|
private String keyID;
|
||||||
|
@XmlElement
|
||||||
|
private String siteName;
|
||||||
|
@XmlElement
|
||||||
|
private Integer siteCounter;
|
||||||
|
@XmlElement
|
||||||
|
private String siteType;
|
||||||
|
@XmlElement
|
||||||
|
private String siteVariant;
|
||||||
|
@XmlElement
|
||||||
|
private String siteContext;
|
||||||
|
@XmlElement
|
||||||
|
private String result;
|
||||||
|
|
||||||
|
private transient Case parentCase;
|
||||||
|
|
||||||
|
public void setTests(MPWTests tests) {
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
parentCase = tests.getCase( parent );
|
||||||
|
fullName = ifNotNullElse( fullName, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase.getFullName();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
masterPassword = ifNotNullElse( masterPassword, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return new String( parentCase.getMasterPassword() );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase.getKeyID();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase.getSiteName();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteCounter = ifNotNullElse( siteCounter, new NNSupplier<Integer>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public Integer get() {
|
||||||
|
return parentCase.getSiteCounter();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteType = ifNotNullElse( siteType, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase.getSiteType().name();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteVariant = ifNotNullElse( siteVariant, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase.getSiteVariant().name();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
siteContext = ifNotNullElseNullable( siteContext, new NSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase.getSiteContext();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
result = ifNotNullElse( result, new NNSupplier<String>() {
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return parentCase.getResult();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Case getParentCase() {
|
||||||
|
return parentCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullName() {
|
||||||
|
return fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char[] getMasterPassword() {
|
||||||
|
return masterPassword == null? null: masterPassword.toCharArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKeyID() {
|
||||||
|
return keyID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSiteName() {
|
||||||
|
return siteName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSiteCounter() {
|
||||||
|
return ifNotNullElse( siteCounter, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public MPSiteType getSiteType() {
|
||||||
|
return MPSiteType.forName( siteType );
|
||||||
|
}
|
||||||
|
|
||||||
|
public MPSiteVariant getSiteVariant() {
|
||||||
|
return MPSiteVariant.forName( siteVariant );
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSiteContext() {
|
||||||
|
return siteContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.lyndir.masterpassword;
|
||||||
|
|
||||||
|
import static org.testng.Assert.*;
|
||||||
|
|
||||||
|
import com.google.common.io.Resources;
|
||||||
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
|
import java.net.URL;
|
||||||
|
import javax.xml.bind.JAXBContext;
|
||||||
|
import org.testng.annotations.BeforeMethod;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
|
||||||
|
public class MasterKeyTest {
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
|
private static final Logger logger = Logger.get( MasterKeyTest.class );
|
||||||
|
|
||||||
|
private MPWTests tests;
|
||||||
|
private MPWTests.Case defaultCase;
|
||||||
|
|
||||||
|
@BeforeMethod
|
||||||
|
public void setUp()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
URL testCasesResource = Resources.getResource( "mpw_tests.xml" );
|
||||||
|
tests = (MPWTests) JAXBContext.newInstance( MPWTests.class ).createUnmarshaller().unmarshal( testCasesResource );
|
||||||
|
for (MPWTests.Case testCase : tests.getCases())
|
||||||
|
testCase.setTests( tests );
|
||||||
|
defaultCase = tests.getCase( MPWTests.ID_DEFAULT );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncode()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
for (MPWTests.Case testCase : tests.getCases()) {
|
||||||
|
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
||||||
|
assertEquals(
|
||||||
|
masterKey.encode( testCase.getSiteName(), testCase.getSiteType(), testCase.getSiteCounter(), testCase.getSiteVariant(),
|
||||||
|
testCase.getSiteContext() ), testCase.getResult(), "Failed test case: " + testCase );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetUserName()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
assertEquals( MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() ).getFullName(),
|
||||||
|
defaultCase.getFullName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetKeyID()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
for (MPWTests.Case testCase : tests.getCases()) {
|
||||||
|
MasterKey masterKey = MasterKey.create( testCase.getFullName(), testCase.getMasterPassword() );
|
||||||
|
assertEquals( CodeUtils.encodeHex( masterKey.getKeyID() ), testCase.getKeyID(), "Failed test case: " + testCase );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidate()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
try {
|
||||||
|
MasterKey masterKey = MasterKey.create( defaultCase.getFullName(), defaultCase.getMasterPassword() );
|
||||||
|
masterKey.invalidate();
|
||||||
|
masterKey.encode( defaultCase.getSiteName(), defaultCase.getSiteType(), defaultCase.getSiteCounter(),
|
||||||
|
defaultCase.getSiteVariant(), defaultCase.getSiteContext() );
|
||||||
|
assertTrue( false, "Master key should have been invalidated, but was still usable." );
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<configuration scan="false">
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||||
|
<Pattern>%-8relative %22c{0} [%-5level] %msg%n</Pattern>
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="com.lyndir.masterpassword" level="${mp.log.level:-TRACE}" />
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<tests>
|
||||||
|
<case id="default">
|
||||||
|
<fullName>Robert Lee Mitchell</fullName>
|
||||||
|
<masterPassword>banana colored duckling</masterPassword>
|
||||||
|
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
|
||||||
|
<siteName>masterpasswordapp.com</siteName>
|
||||||
|
<siteCounter>1</siteCounter>
|
||||||
|
<siteType>GeneratedLong</siteType>
|
||||||
|
<siteVariant>Password</siteVariant>
|
||||||
|
<result>Jejr5[RepuSosp</result>
|
||||||
|
</case>
|
||||||
|
<case id="mb_fullName" parent="default">
|
||||||
|
<fullName>⛄</fullName>
|
||||||
|
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
|
||||||
|
<result>NopaDajh8=Fene</result>
|
||||||
|
</case>
|
||||||
|
<case id="mb_masterPassword" parent="default">
|
||||||
|
<masterPassword>⛄</masterPassword>
|
||||||
|
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
|
||||||
|
<result>QesuHirv5-Xepl</result>
|
||||||
|
</case>
|
||||||
|
<case id="mb_siteName" parent="default">
|
||||||
|
<siteName>⛄</siteName>
|
||||||
|
<result>LiheCuwhSerz6)</result>
|
||||||
|
</case>
|
||||||
|
<case id="loginName" parent="default">
|
||||||
|
<siteVariant>Login</siteVariant>
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>wohzaqage</result>
|
||||||
|
</case>
|
||||||
|
<case id="securityAnswer" parent="default">
|
||||||
|
<siteVariant>Answer</siteVariant>
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>xin diyjiqoja hubu</result>
|
||||||
|
</case>
|
||||||
|
<case id="securityAnswer_context" parent="securityAnswer">
|
||||||
|
<siteContext>question</siteContext>
|
||||||
|
<result>xogx tem cegyiva jab</result>
|
||||||
|
</case>
|
||||||
|
<case id="type_maximum" parent="default">
|
||||||
|
<siteType>GeneratedMaximum</siteType>
|
||||||
|
<result>W6@692^B1#&@gVdSdLZ@</result>
|
||||||
|
</case>
|
||||||
|
<case id="type_medium" parent="default">
|
||||||
|
<siteType>GeneratedMedium</siteType>
|
||||||
|
<result>Jej2$Quv</result>
|
||||||
|
</case>
|
||||||
|
<case id="type_basic" parent="default">
|
||||||
|
<siteType>GeneratedBasic</siteType>
|
||||||
|
<result>WAo2xIg6</result>
|
||||||
|
</case>
|
||||||
|
<case id="type_short" parent="default">
|
||||||
|
<siteType>GeneratedShort</siteType>
|
||||||
|
<result>Jej2</result>
|
||||||
|
</case>
|
||||||
|
<case id="type_pin" parent="default">
|
||||||
|
<siteType>GeneratedPIN</siteType>
|
||||||
|
<result>7662</result>
|
||||||
|
</case>
|
||||||
|
<case id="type_name" parent="default">
|
||||||
|
<siteType>GeneratedName</siteType>
|
||||||
|
<result>jejraquvo</result>
|
||||||
|
</case>
|
||||||
|
<case id="type_phrase" parent="default">
|
||||||
|
<siteType>GeneratedPhrase</siteType>
|
||||||
|
<result>jejr quv cabsibu tam</result>
|
||||||
|
</case>
|
||||||
|
<case id="counter_ceiling" parent="default">
|
||||||
|
<siteCounter>4294967295</siteCounter>
|
||||||
|
<result>XambHoqo6[Peni</result>
|
||||||
|
</case>
|
||||||
|
</tests>
|
||||||
|
|
||||||
@@ -2,17 +2,17 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.lyndir.masterpassword"
|
package="com.lyndir.masterpassword"
|
||||||
android:versionCode="1"
|
android:versionCode="1"
|
||||||
android:versionName="GIT-SNAPSHOT">
|
android:versionName="2.2">
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="14"
|
android:minSdkVersion="19"
|
||||||
android:targetSdkVersion="19" />
|
android:targetSdkVersion="21" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@drawable/icon"
|
android:icon="@drawable/icon"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:allowBackup="true">
|
android:allowBackup="true">
|
||||||
<activity android:name=".EmergencyActivity">
|
<activity android:name=".EmergencyActivity" android:theme="@style/MPTheme">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# File used by Eclipse to determine the target system
|
|
||||||
# Project target.
|
|
||||||
target=android-16
|
|
||||||
@@ -7,13 +7,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword</artifactId>
|
<artifactId>masterpassword</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<name>Master Password Android</name>
|
<name>Master Password Android</name>
|
||||||
<description>An Android application to the Master Password algorithm</description>
|
<description>An Android application to the Master Password algorithm</description>
|
||||||
|
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
|
||||||
<artifactId>masterpassword-android</artifactId>
|
<artifactId>masterpassword-android</artifactId>
|
||||||
<packaging>apk</packaging>
|
<packaging>apk</packaging>
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
<skip>false</skip>
|
<skip>false</skip>
|
||||||
</zipalign>
|
</zipalign>
|
||||||
<sdk>
|
<sdk>
|
||||||
<platform>19</platform>
|
<platform>21</platform>
|
||||||
</sdk>
|
</sdk>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
@@ -39,9 +38,32 @@
|
|||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>sign</id>
|
<id>release</id>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
||||||
|
<artifactId>android-maven-plugin</artifactId>
|
||||||
|
|
||||||
|
<configuration>
|
||||||
|
<sign>
|
||||||
|
<debug>false</debug>
|
||||||
|
</sign>
|
||||||
|
</configuration>
|
||||||
|
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>manifest-update</id>
|
||||||
|
<phase>process-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>manifest-update</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<manifestVersionCodeUpdateFromVersion>true</manifestVersionCodeUpdateFromVersion>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jarsigner-plugin</artifactId>
|
<artifactId>maven-jarsigner-plugin</artifactId>
|
||||||
@@ -54,14 +76,14 @@
|
|||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
<inherited>true</inherited>
|
<inherited>true</inherited>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archiveDirectory></archiveDirectory>
|
<archiveDirectory />
|
||||||
<includes>
|
<includes>
|
||||||
<include>target/*.apk</include>
|
<include>target/*.apk</include>
|
||||||
</includes>
|
</includes>
|
||||||
<keystore>release.jks</keystore>
|
<keystore>release.jks</keystore>
|
||||||
<storepass>${env.PASSWORD}</storepass>
|
<storepass>${env.PASSWORD}</storepass>
|
||||||
<keypass>${env.PASSWORD}</keypass>
|
<keypass>${env.PASSWORD}</keypass>
|
||||||
<alias>android</alias>
|
<alias>masterpassword-android</alias>
|
||||||
<arguments>
|
<arguments>
|
||||||
<argument>-sigalg</argument><argument>MD5withRSA</argument>
|
<argument>-sigalg</argument><argument>MD5withRSA</argument>
|
||||||
<argument>-digestalg</argument><argument>SHA1</argument>
|
<argument>-digestalg</argument><argument>SHA1</argument>
|
||||||
@@ -70,16 +92,6 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
|
||||||
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
|
|
||||||
<artifactId>android-maven-plugin</artifactId>
|
|
||||||
<inherited>true</inherited>
|
|
||||||
<configuration>
|
|
||||||
<sign>
|
|
||||||
<debug>false</debug>
|
|
||||||
</sign>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
@@ -92,7 +104,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lyndir.masterpassword</groupId>
|
<groupId>com.lyndir.masterpassword</groupId>
|
||||||
<artifactId>masterpassword-algorithm</artifactId>
|
<artifactId>masterpassword-algorithm</artifactId>
|
||||||
<version>GIT-SNAPSHOT</version>
|
<version>2.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -114,11 +126,10 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lambdaworks</groupId>
|
<groupId>com.lambdaworks</groupId>
|
||||||
<artifactId>libscrypt</artifactId>
|
<artifactId>scrypt</artifactId>
|
||||||
<version>1.4.0</version>
|
<version>1.4.0-android</version>
|
||||||
<type>so</type>
|
<type>jar</type>
|
||||||
<classifier>android</classifier>
|
<classifier>native</classifier>
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
1
MasterPassword/Java/masterpassword-android/release.jks
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks
|
||||||
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB |