2
0

Compare commits

...

92 Commits

Author SHA1 Message Date
Maarten Billemont
73372f507a [maven-release-plugin] prepare release 2.2-android 2015-02-05 20:28:25 -05:00
Maarten Billemont
feddd038e3 Also sign masterpassword-gui 2015-02-05 20:27:09 -05:00
Maarten Billemont
3f6773f3a9 Android release preparation. 2015-02-05 17:29:29 -05:00
Maarten Billemont
12b1610dc7 Remove unavailable resources + update graphics. 2015-02-05 16:34:04 -05:00
Maarten Billemont
e20b33a051 Prepare for making Android releases. 2015-02-05 13:43:57 -05:00
Maarten Billemont
b84ae532f2 Some generic code fix-ups throughout. 2015-02-05 13:14:17 -05:00
Maarten Billemont
145008406d Big overhaul for proper site-specific algorithm support and big Android UI update. 2015-02-05 00:56:24 -05:00
Maarten Billemont
a6ab9b9194 Moar UI work on the Java app + support for per-site algorithm versioning. 2015-02-04 19:51:38 -05:00
Maarten Billemont
78c593fc08 Many UI improvements to the Java GUI. 2015-02-04 11:25:18 -05:00
Maarten Billemont
5b08149ca6 Fix a crash on older OS X versions. 2015-02-02 14:48:12 -05:00
Maarten Billemont
58afc19c26 Make string constants C constants, not macros. 2015-02-02 14:47:16 -05:00
Maarten Billemont
b3109187e9 Some project cleanup. 2015-01-31 11:08:22 -05:00
Maarten Billemont
a6e7a749bf Android improvements.
[UPDATED]   Opal API
[ADDED]     Scrypt native binaries for more archs.
[IMPROVED]  Android activity secure.
[FIXED]     White background on buttons for some devices.
[IMPROVED]  Android layout.
[WIP]       Remember password on Android.
2015-01-31 10:55:08 -05:00
Maarten Billemont
ca5d83d40c Put LDFLAGS after .o files that use them for compatibility with the 11.10+ linker. 2015-01-29 14:37:15 -05:00
Maarten Billemont
285813324f mpw-js is now back in sync with Tom's repo. 2015-01-23 08:31:50 -05:00
Maarten Billemont
d4b772b467 Update to Tom's latest mpw-js to fix issues with maximum security passwords. 2015-01-23 08:14:13 -05:00
Maarten Billemont
f392ad4053 Merge branch 'master' of github.com:Lyndir/MasterPassword 2015-01-21 11:12:23 -05:00
Maarten Billemont
35990f3bdd Update Java GUI. 2015-01-21 09:11:14 -05:00
Maarten Billemont
3932857c21 Fix for not being able to copy the password from the password field. 2015-01-21 09:07:57 -05:00
Maarten Billemont
6f771a972b his -> their. 2015-01-20 10:37:43 -05:00
Maarten Billemont
806a07135a Update 2.1-cli4 again. 2015-01-19 23:32:26 -05:00
Maarten Billemont
f6b2287778 Build fixes for iOS and Mac. 2015-01-19 23:30:19 -05:00
Maarten Billemont
f4e90bb839 Update 2.1-cli4 2015-01-19 23:23:10 -05:00
Maarten Billemont
21630e919b Standardize includes for other POSIX platforms. 2015-01-19 23:21:10 -05:00
Maarten Billemont
ae74ab6906 Site references to mpw-cli C packages. 2015-01-19 23:07:49 -05:00
Maarten Billemont
caf361cd10 Rename mpw formula. 2015-01-19 21:54:50 -05:00
Maarten Billemont
aeedc1946e Some audit fixes to mpw.rb. 2015-01-19 21:53:41 -05:00
Maarten Billemont
93ae31f679 An initial Homebrew formula for installing mpw. 2015-01-19 21:48:44 -05:00
Maarten Billemont
d5ff215da2 Support for passing the master password non-interactively for testing. 2015-01-19 21:34:54 -05:00
Maarten Billemont
b34f7377da Handle dependencies unpacked by a package manager. 2015-01-19 20:58:53 -05:00
Maarten Billemont
0c2e182039 Release a new C CLI and update links. 2015-01-19 17:22:05 -05:00
Maarten Billemont
438daf27ee Use anchor tags for anchors to avoid id collision. 2015-01-19 00:23:27 -05:00
Maarten Billemont
aa6634970a Fix anchors. 2015-01-19 00:17:14 -05:00
Maarten Billemont
9052416786 Update Java Desktop app. 2015-01-19 00:15:33 -05:00
Maarten Billemont
9d19eaf667 Some anchors. 2015-01-19 00:13:28 -05:00
Maarten Billemont
7ae9afa63a Merge commit '3d856b3' 2015-01-17 13:51:31 -05:00
Maarten Billemont
3d856b3773 Warnings update. 2015-01-17 13:51:22 -05:00
Maarten Billemont
7617b2382a Fix V0 C implementation. 2015-01-17 11:17:16 -05:00
Maarten Billemont
a03dcf6859 Ability to pass the algorithm version on the CLI. 2015-01-16 00:25:18 -05:00
Maarten Billemont
57769ba199 Algorithm versions in C and wire ObjC into C, remove ObjC algorithm implementation. 2015-01-15 17:43:41 -05:00
Maarten Billemont
6304b3a619 Looks like the default close operation is hide and WINDOW_CLOSING is only an interactive request to close the window. 2015-01-04 11:28:30 -05:00
Maarten Billemont
d1649f3c33 Just dispose on close and scope executors to a window, clean up on window hide. 2015-01-03 14:25:20 -05:00
Maarten Billemont
80f507b4cc Remove VERSION from project. 2015-01-02 13:41:29 -05:00
Maarten Billemont
f8a665db65 use libscryptenc-ios-sim for simulator builds. 2015-01-02 12:51:23 -05:00
Maarten Billemont
b15f2a8a26 Properly invoke the default close operation when dismissing the password frame. 2015-01-02 12:19:49 -05:00
Maarten Billemont
e9094097a2 Mask the generated password by default, provide a check box to unmask it. 2014-12-31 14:46:44 -05:00
Maarten Billemont
bea6ac5e68 Attempt to fix copy issue when Java app closes after copying. 2014-12-31 14:04:14 -05:00
Maarten Billemont
778533ac7f Fix log-in after entering the wrong master password. 2014-12-31 13:53:28 -05:00
Maarten Billemont
83fcde5bd0 Add new Mac OS X binary. 2014-12-31 13:44:02 -05:00
Maarten Billemont
c9ec5874d3 Add support for Crashlytics to Mac OS X app. 2014-12-31 13:02:23 -05:00
Maarten Billemont
4ce5fd25bc Allow importing without a KeyID, fix a possible deadlock and fix showing error messages + replace light font with regular on non-retina. 2014-12-29 16:37:58 -05:00
Maarten Billemont
1ed28ebc9b Update Master Password for Java GUI. 2014-12-29 16:32:48 -05:00
Maarten Billemont
c03199f7e5 Update directory to mpw.d and fix issue that caused only one user to be visible in the drop-down. 2014-12-28 14:46:20 -05:00
Maarten Billemont
9f10bcdec4 Bump cli2 to fix symlink issue. 2014-12-21 23:59:07 -05:00
Maarten Billemont
82c96ddfe3 Update distribute script to include source files for symlinks. 2014-12-21 23:58:33 -05:00
Maarten Billemont
c0fea076b9 Release 2.1-cli2 2014-12-21 23:50:41 -05:00
Maarten Billemont
b779ff5d1c Added C tests for mpw_tests.xml 2014-12-21 23:45:19 -05:00
Maarten Billemont
73c10906e3 Some better memory maintenance. 2014-12-21 12:37:21 -05:00
Maarten Billemont
0ccd545dd4 More restructuring and rewriting of the C code. 2014-12-20 14:30:34 -05:00
Maarten Billemont
49da0b47c7 Complete an initial rewrite/restructure of mpw.c 2014-12-20 00:21:03 -05:00
Maarten Billemont
672b28a5b7 Restructure, split up mpw cli from mpw core. 2014-12-19 23:15:32 -05:00
Maarten Billemont
2dbada3c7c Update build script to install header files in a shared include path, akin to how ObjC does. 2014-12-19 09:00:38 -05:00
Maarten Billemont
3dbc105fbd Update C code for inclusion in ObjC and update scrypt to colin's latest code. 2014-12-19 00:03:54 -05:00
Maarten Billemont
43d55211b0 Revert "Revert accidentally committed changes."
This reverts commit a62ae8c757.
2014-12-18 17:47:38 -05:00
Maarten Billemont
f170e9df69 Update Java GUI. 2014-12-17 00:44:48 -05:00
Maarten Billemont
1fbb6b0754 Clear the password input field and pop a warning when entering bad master password. 2014-12-17 00:31:34 -05:00
Maarten Billemont
4c526d6f08 Fixed a bug causing exceptions loading maximum security passwords. 2014-12-17 00:18:02 -05:00
Maarten Billemont
a62ae8c757 Revert accidentally committed changes. 2014-12-16 23:07:31 -05:00
Maarten Billemont
f2eb53569b Use ~/.mpwrc since ~/.mpw is used by other tools.. 2014-12-16 22:14:52 -05:00
Maarten Billemont
c2a6a3d035 Full ability to load, add and autocomplete sites from history. 2014-12-16 22:13:11 -05:00
Maarten Billemont
97dcc65eac Update install scripts for new env var names. 2014-12-16 08:54:58 -05:00
Maarten Billemont
1bd76dbb61 Read avatar from user export and allow user to modify it by clicking the picture. 2014-12-12 17:25:32 -05:00
Maarten Billemont
0fdf894bf0 Fix issue with passing context in Java algorithm and test case. 2014-12-12 10:54:28 -05:00
Maarten Billemont
19202e07d4 Merge branch 'master' of github.com:Lyndir/MasterPassword 2014-12-12 10:14:09 -05:00
Maarten Billemont
84b624aea2 WIP - integrate user and site storage through export files into Java Swing GUI. 2014-12-11 20:35:19 -05:00
Maarten Billemont
c7ac5087b3 Fix test cases loginName, securityAnswer and securityAnswer_context 2014-12-10 10:34:55 -05:00
Maarten Billemont
4ff8cd6d90 A persistence model for the Java implementation and ability to parse in and write out export files. 2014-12-08 11:11:29 -05:00
Maarten Billemont
3f4558da2b Standardize on a naming scheme: cipher -> template, userName -> fullName, element -> site. 2014-12-05 17:59:10 -05:00
Maarten Billemont
b976e79b0f Fix some mpw.c link errors.
[FIXED]     Libraries should be specified after the objects that need them for some compilers apparently..
[FIXED]     bool type requires stdbool.h, disable more color code when COLOR not defined.
[UPDATED]   Disable mpw_color by default as it doesn't link cleanly on some Linux'es for now..
2014-12-05 13:51:02 -05:00
Maarten Billemont
3d064fa68d A full test script for various inputs and a Java TestNG implementation that tests it. 2014-12-05 02:17:28 -05:00
Maarten Billemont
1a1e024178 Fix Java unit test expected values from C implementation's output. 2014-12-04 00:35:21 -05:00
Maarten Billemont
4876d62b56 Copy the string into masterPassword because the line is getting free'd. 2014-12-04 00:28:38 -05:00
Maarten Billemont
8006b7096f Make Java and C debug output comparable. 2014-12-03 23:36:18 -05:00
Maarten Billemont
a82ce7310d Remove plist dependency, fix length bug, import ciphers.plist.
[REMOVED]   Java code no longer depends on ciphers.plist and net.sf.plist.
[ADDED]     Java code now explicitly defines the algorithm's templates.
[FIXED]     Java code now properly counts the site name and user name's byte length.
[FIXED]     Java code now explicitly uses 32-bit integers.
2014-12-03 00:46:00 -05:00
Maarten Billemont
ae08cb62c5 Add the env variables to the usage output. 2014-12-03 00:40:54 -05:00
Maarten Billemont
c48fba6c01 No color fixes & malloc bug in .mpw reading.
[FIXED]     We weren't properly excluding all dependencies on ncurses when colors are not enabled.
[FIXED]     There was a memory realloc bug when reading multiple lines from ~/.mpw.
2014-12-02 20:46:53 -05:00
Maarten Billemont
3db25e7e3b Some more attempts at being better at memory handling + remove useless and untested cygwin stuff. 2014-11-23 14:01:29 -05:00
Maarten Billemont
1f7a49378b Allocate enough space for the NUL c-string delimitor. 2014-11-23 13:44:37 -05:00
Maarten Billemont
37ec21f5be Disable colors when output is not a terminal. 2014-11-23 13:33:09 -05:00
Maarten Billemont
2b8498f569 Support for patching dependencies + ARM patch for bcrypt. 2014-11-21 09:39:30 -05:00
Maarten Billemont
5c4fc61a12 Bump bashlib. 2014-11-21 08:15:51 -05:00
Maarten Billemont
c0ec65bbae Make identicon color an optional feature and specify the dependency. 2014-11-21 08:06:29 -05:00
227 changed files with 6645 additions and 2948 deletions

4
.gitignore vendored
View File

@@ -28,14 +28,12 @@ Press/MasterPassword_PressKit/MasterPassword_pressrelease_*.pdf
/sendipa/*
!/sendipa/sendipa.conf
# Java
MasterPassword/Java/**/target
# C
MasterPassword/C/VERSION
MasterPassword/C/*.o
MasterPassword/C/mpw-*.tar.gz
MasterPassword/C/mpw
MasterPassword/C/mpw-bench
MasterPassword/C/mpw-tests
MasterPassword/C/lib/*/*
!MasterPassword/C/lib/*/.source

2
.gitmodules vendored
View File

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

View File

@@ -2,6 +2,7 @@
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<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="ImplicitIntegerAndEnumConversion" enabled="false" level="WARNING" enabled_by_default="false" />
<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="OCUnusedMacroInspection" 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="UnavailableInDeploymentTarget" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="UnusedLocalVariable" enabled="false" level="WARNING" enabled_by_default="false" />

2
External/Pearl vendored

View File

@@ -27,7 +27,7 @@
# ______________________________________________________________________
# | |
# | .:: TABLE OF CONTENTS ::. |
# | .: TABLE OF CONTENTS :. |
# |______________________________________________________________________|
#
# chr decimal
@@ -66,12 +66,6 @@
# readwhile command [args]
# 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 an event at a certain importance level.
# 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.
@@ -177,7 +171,7 @@ genToc() {
# ______________________________________________________________________
# | |
# | .:: GLOBAL DECLARATIONS ::. |
# | .: GLOBAL DECLARATIONS :. |
# |______________________________________________________________________|
# Variables for convenience sequences.
@@ -190,8 +184,8 @@ runner=( '> >' \
# Variables for terminal requests.
[[ -t 2 && $TERM != dumb ]] && {
COLUMNS=$( tput cols || tput co ) # Columns in a line
LINES=$( tput lines || tput li ) # Lines on screen
COLUMNS=$({ tput cols || tput co;} 2>&3) # Columns in a line
LINES=$({ tput lines || tput li;} 2>&3) # Lines on screen
alt=$( tput smcup || tput ti ) # Start alt display
ealt=$( tput rmcup || tput te ) # End alt display
hide=$( tput civis || tput vi ) # Hide cursor
@@ -230,7 +224,7 @@ runner=( '> >' \
tput eA; tput as;
tput ac; tput ae; } ) # Drawing characters
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 ____________________________________________________________|
#
@@ -1566,7 +1543,7 @@ stackTrace() {
# ______________________________________________________________________
# | |
# | .:: ENTRY POINT ::. |
# | .: ENTRY POINT :. |
# |______________________________________________________________________|
# Make sure this file is sourced and not executed.
@@ -1586,6 +1563,6 @@ stackTrace() {
}
:
: .:: END SOURCING ::.
: .: END SOURCING :.
: ______________________________________________________________________
:

View File

@@ -9,6 +9,8 @@
# try ./build -lrt instead.
# - 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.
# - Take a look at the "Optional features" section. Some features have dependencies,
# either make sure you have them or disable those features.
#
# BUGS
# masterpassword@lyndir.com
@@ -31,10 +33,14 @@ else
# Modify here or override using targets='mpw mpw-bench' ./build
targets=(
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
# Optional features.
mpw_color=0 # Colorized Identicon, requires libncurses-dev
### DEPENDENCIES
@@ -75,11 +81,15 @@ unpack() {
mv "$files"/* .
rmdir "$files"
fi
touch .unpacked
}
fetchSource() (
source .source
if [[ $pkg && -e "${pkg##*/}" ]]; then
if [[ -e .unpacked ]]; then
true
elif [[ $pkg && -e "${pkg##*/}" ]]; then
files=( !("${pkg##*/}") )
[[ -e $files ]] || {
echo
@@ -128,12 +138,21 @@ fetchSource() (
echo >&2 "error: into: $PWD"
exit 1
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() {
echo
echo "Checking dependency: $1..."
[[ -e "lib/$1/.built" ]] && return
[[ -e "lib/include/$1" ]] && return
pushd "lib/$1"
fetchSource
@@ -169,7 +188,8 @@ depend() {
fi
make
date > .built
install -d "../include/$1/"
find . -name '*.h' -exec install -m 444 {} "../include/$1/" \;
else
echo >&2 "error: Don't know how to build: $1"
exit 1
@@ -186,13 +206,9 @@ mpw() {
echo "Building target: $target..."
CFLAGS=(
# include paths
-I"lib/scrypt/lib" -I"lib/scrypt/libcperciva"
-I"lib/include"
)
LDFLAGS=(
# library paths
-L"." -L"lib/scrypt"
# link libraries
-l"crypto" -l"curses"
# scrypt
"lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o"
@@ -200,10 +216,19 @@ mpw() {
"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"
)
# optional features
(( mpw_color )) && CFLAGS+=( -DCOLOR ) LDFLAGS+=( -l"curses" )
cc "${CFLAGS[@]}" -c types.c -o types.o "$@"
cc "${CFLAGS[@]}" "${LDFLAGS[@]}" "types.o" mpw.c -o mpw "$@"
cc "${CFLAGS[@]}" "$@" -c mpw-algorithm.c -o mpw-algorithm.o
cc "${CFLAGS[@]}" "$@" -c mpw-types.c -o mpw-types.o
cc "${CFLAGS[@]}" "$@" -c mpw-util.c -o mpw-util.o
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
"${LDFLAGS[@]}" "mpw-cli.c" -o "mpw"
echo "done! Now run ./install or use ./mpw"
}
@@ -217,15 +242,9 @@ mpw-bench() {
echo "Building target: $target..."
CFLAGS=(
# include paths
-I"lib/scrypt/lib" -I"lib/scrypt/libcperciva"
-I"lib/bcrypt"
-I"lib/include"
)
LDFLAGS=(
# library paths
-L"." -L"lib/scrypt"
-L"lib/bcrypt"
# libraries
-l"crypto"
# scrypt
"lib/scrypt/scrypt-crypto_aesctr.o"
"lib/scrypt/scrypt-sha256.o"
@@ -238,14 +257,52 @@ mpw-bench() {
"lib/bcrypt/crypt_gensalt.o"
"lib/bcrypt/wrapper.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[@]}" "${LDFLAGS[@]}" "types.o" mpw-bench.c -o mpw-bench "$@"
cc "${CFLAGS[@]}" "$@" "mpw-algorithm.o" "mpw-types.o" "mpw-util.o" \
"${LDFLAGS[@]}" "mpw-bench.c" -o "mpw-bench"
echo "done! Now use ./mpw-bench"
}
### 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
haslib() {

View File

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

View File

@@ -45,8 +45,8 @@ fi
echo
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
printf 'export MP_USERNAME=%q\n' "$MP_USERNAME" >> ~/.bashrc
if MP_FULLNAME=$(ask "Your full name:") && [[ $MP_FULLNAME ]] ; then
printf 'export MP_FULLNAME=%q\n' "$MP_FULLNAME" >> ~/.bashrc
fi
echo

View 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

View File

@@ -1,3 +1,4 @@
home=http://www.openwall.com/crypt/
pkg=http://www.openwall.com/crypt/crypt_blowfish-1.3.tar.gz
pkg_sha256=83fa01fca6996fe8d882b7f8e9ba0305a5664936100b01481ea3c6a8ce8d72fd
patches=(arm)

View 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;
}
}

View 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);

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 <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 <string.h>
#include <errno.h>
#include <sys/time.h>
#include <alg/sha256.h>
#include <crypto/crypto_scrypt.h>
#include <ow-crypt.h>
#include "types.h"
#include <scrypt/sha256.h>
#include <bcrypt/ow-crypt.h>
#include "mpw-algorithm.h"
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
@@ -22,166 +24,84 @@
#define MP_dkLen 64
#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[]) {
char *userName = "Robert Lee Mitchel";
char *masterPassword = "banana colored duckling";
char *siteName = "masterpasswordapp.com";
uint32_t siteCounter = 1;
MPElementType siteType = MPElementTypeGeneratedLong;
// Start MP
const char *fullName = "Robert Lee Mitchel";
const char *masterPassword = "banana colored duckling";
const char *siteName = "masterpasswordapp.com";
const uint32_t siteCounter = 1;
const MPSiteType siteType = MPSiteTypeGeneratedLong;
const MPSiteVariant siteVariant = MPSiteVariantPassword;
const char *siteContext = NULL;
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) {
// Calculate the master key salt.
char *mpNameSpace = "com.lyndir.masterpassword";
const uint32_t n_userNameLength = htonl(strlen(userName));
const size_t masterKeySaltLength = strlen(mpNameSpace) + sizeof(n_userNameLength) + strlen(userName);
char *masterKeySalt = malloc( masterKeySaltLength );
if (!masterKeySalt) {
fprintf(stderr, "Could not allocate master key salt: %d\n", errno);
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));
const uint8_t *masterKey = mpw_masterKeyForUser(
fullName, masterPassword, MPAlgorithmVersionCurrent );
if (!masterKey)
ftl( "Could not allocate master key: %d\n", errno );
free( (void *)mpw_passwordForSite(
masterKey, siteName, siteType, siteCounter, siteVariant, siteContext, MPAlgorithmVersionCurrent ) );
free( (void *)masterKey );
if (i % 1 == 0)
fprintf( stderr, "\rmpw: iteration %d / %d..", i, iterations );
}
// 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 );
const double mpwSpeed = mpw_showSpeed( startTime, iterations, "mpw" );
// Start SHA-256
if (gettimeofday(&startTime, NULL) != 0) {
fprintf(stderr, "Could not get time: %d\n", errno);
return 1;
}
iterations = 50000000;
uint8_t hash[32];
mpw_getTime( &startTime );
for (int i = 0; i < iterations; ++i) {
SHA256_Buf(masterPassword, strlen(masterPassword), hash);
SHA256_Buf( masterPassword, strlen( masterPassword ), hash );
if (i % 1000 == 0)
fprintf( stderr, "\rsha256: iteration %d / %d..", i, iterations );
}
// 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 );
const double sha256Speed = mpw_showSpeed( startTime, iterations, "sha256" );
// Start BCrypt
if (gettimeofday(&startTime, NULL) != 0) {
fprintf(stderr, "Could not get time: %d\n", errno);
return 1;
}
int bcrypt_cost = 9;
iterations = 600;
mpw_getTime( &startTime );
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)
fprintf( stderr, "\rbcrypt (cost %d): iteration %d / %d..", bcrypt_cost, i, iterations );
}
// 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 );
const double bcrypt9Speed = mpw_showSpeed( startTime, iterations, "bcrypt9" );
// Summarize.
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 bcrypt (cost 9)\n", bcrypt9Speed / 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 );
return 0;
}

222
MasterPassword/C/mpw-cli.c Normal file
View 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;
}

View 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;
}

View 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);

View 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;
}

View 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 )];
}

View 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
View 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 ) );
}

View 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);

View File

@@ -17,8 +17,8 @@ mpw() {
:| _copy 2>/dev/null
# 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.
printf %s "$(MP_USERNAME=$MP_USERNAME command mpw "$@")" | _copy
printf %s "$(MP_FULLNAME=$MP_FULLNAME command mpw "$@")" | _copy
}

View File

@@ -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;
}

View File

@@ -0,0 +1 @@
../Java/masterpassword-algorithm/src/test/resources/mpw_tests.xml

View File

@@ -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;
}

View File

@@ -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
View File

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

View File

@@ -7,13 +7,12 @@
<parent>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword</artifactId>
<version>GIT-SNAPSHOT</version>
<version>2.2</version>
</parent>
<name>Master Password Algorithm Implementation</name>
<description>The implementation of the Master Password algorithm</description>
<groupId>com.lyndir.masterpassword</groupId>
<artifactId>masterpassword-algorithm</artifactId>
<packaging>jar</packaging>
@@ -24,26 +23,33 @@
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-system</artifactId>
<version>1.6-p6</version>
<version>1.6-p8</version>
</dependency>
<dependency>
<groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-crypto</artifactId>
<version>1.6-p6</version>
<version>1.6-p8</version>
</dependency>
<!-- EXTERNAL DEPENDENCIES -->
<dependency>
<groupId>net.sf.plist</groupId>
<artifactId>property-list</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.lambdaworks</groupId>
<artifactId>scrypt</artifactId>
<version>1.4.0</version>
</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>
</project>

View File

@@ -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,
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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() );
}
}

View File

@@ -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;
}
}

View File

@@ -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 );
}
}

View File

@@ -1,9 +1,10 @@
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import java.util.List;
import java.util.Map;
/**
@@ -13,20 +14,21 @@ import java.util.Map;
*/
public class MPTemplate extends MetaObject {
private final String templateString;
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();
for (int i = 0; i < template.length(); ++i)
builder.add( characterClasses.get( template.charAt( i ) ) );
ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.builder();
for (int i = 0; i < templateString.length(); ++i)
builder.add( MPTemplateCharacterClass.forIdentifier( templateString.charAt( i ) ) );
this.template = builder.build();
this.templateString = templateString;
template = builder.build();
}
public MPTemplate(final List<MPTemplateCharacterClass> template) {
this.template = template;
public String getTemplateString() {
return templateString;
}
public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {
@@ -38,4 +40,9 @@ public class MPTemplate extends MetaObject {
return template.size();
}
@Override
public String toString() {
return strf( "{MPTemplate: %s}", templateString );
}
}

View File

@@ -1,7 +1,6 @@
package com.lyndir.masterpassword;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import com.lyndir.lhunath.opal.system.util.ObjectMeta;
import com.lyndir.lhunath.opal.system.logging.Logger;
/**
@@ -9,16 +8,29 @@ import com.lyndir.lhunath.opal.system.util.ObjectMeta;
*
* @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;
@ObjectMeta(useFor = { })
private final char[] characters;
public MPTemplateCharacterClass(final char identifier, final char[] characters) {
MPTemplateCharacterClass(final char identifier, final String characters) {
this.identifier = identifier;
this.characters = characters;
this.characters = characters.toCharArray();
}
public char getIdentifier() {
@@ -30,4 +42,12 @@ public class MPTemplateCharacterClass extends MetaObject {
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 );
}
}

View File

@@ -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();
}
}

View File

@@ -1,129 +1,164 @@
package com.lyndir.masterpassword;
import com.google.common.base.Charsets;
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.logging.Logger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import javax.xml.stream.events.Characters;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
/**
* @author lhunath, 2014-08-30
*/
public class MasterKey {
public abstract class MasterKey {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( MasterKey.class );
private static final int MP_N = 32768;
private static final int MP_r = 8;
private static final int MP_p = 2;
private static final int MP_dkLen = 64;
private static final 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 static final Logger logger = Logger.get( MasterKey.class );
private final String userName;
private final byte[] key;
@Nonnull
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;
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 );
}
return create( Version.CURRENT, fullName, masterPassword );
}
public String getUserName() {
@Nonnull
public static MasterKey create(Version version, final String fullName, final char[] masterPassword) {
return userName;
}
public String getKeyID() {
Preconditions.checkState( valid );
return CodeUtils.encodeHex( MP_hash.of( key ) );
}
private byte[] getSubkey(final int subkeyLength) {
Preconditions.checkState( valid );
byte[] subkey = new byte[Math.min( subkeyLength, key.length )];
System.arraycopy( key, 0, subkey, 0, subkey.length );
return subkey;
}
public String encode(final String name, final MPElementType type, int counter) {
Preconditions.checkState( valid );
Preconditions.checkArgument( type.getTypeClass() == MPElementTypeClass.Generated );
Preconditions.checkArgument( !name.isEmpty() );
if (counter == 0)
counter = (int) (System.currentTimeMillis() / (300 * 1000)) * 300;
byte[] nameLengthBytes = ByteBuffer.allocate( Integer.SIZE / Byte.SIZE ).order( MP_byteOrder ).putInt( name.length() ).array();
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 );
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 );
}
return password.toString();
throw new UnsupportedOperationException( "Unsupported version: " + version );
}
protected MasterKey(@NotNull final String fullName) {
this.fullName = fullName;
logger.trc( "fullName: %s", fullName );
}
@Nullable
protected abstract byte[] deriveKey(final char[] masterPassword);
public abstract Version getAlgorithmVersion();
@NotNull
public String getFullName() {
return fullName;
}
@Nonnull
protected byte[] getKey() {
return Preconditions.checkNotNull( masterKey );
}
public byte[] getKeyID() {
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() {
valid = false;
Arrays.fill( key, (byte) 0 );
if (masterKey != null) {
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 );
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +0,0 @@
package com.lyndir.masterpassword.entity;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPElementEntity {
}

View File

@@ -1,10 +0,0 @@
package com.lyndir.masterpassword.entity;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPElementGeneratedEntity extends MPElementEntity {
}

View File

@@ -1,10 +0,0 @@
package com.lyndir.masterpassword.entity;
/**
* <i>07 04, 2012</i>
*
* @author lhunath
*/
public class MPElementStoredEntity extends MPElementEntity {
}

View File

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

View File

@@ -1 +0,0 @@
../../../../../../MasterPassword/Resources/Data/ciphers.plist

View File

@@ -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;
}
}
}

View File

@@ -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) {
}
}
}

View File

@@ -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>

View File

@@ -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#&amp;@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>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
/Users/lhunath/SpiderOak Hive/secret/release-com.lyndir.masterpassword.jks

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

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