2
0

Reorganize core source and add Docker support to CLI.

This commit is contained in:
Maarten Billemont
2018-06-05 20:01:46 -04:00
parent 8e41cba7ac
commit c2aafd8602
215 changed files with 65 additions and 47 deletions

View File

@@ -0,0 +1,110 @@
# Native CLI
The CLI is a command-line terminal interface to the Master Password standard implementation.
To use the app, you'll first need to build it, then install it into your system's PATH.
Start by changing into the CLI application directory:
cd cli
## Building
To build the code to run on your specific system, run the `build` command:
./build
Note that the build depends on your system having certain dependencies already installed.
By default, you'll need to have at least `libsodium`, `libjson-c` and `libncurses` installed.
## Building with docker
To install mpw into a Docker container, make sure you have Docker installed on your system, then run something like:
docker build -f cli/Dockerfile .
## Building with cmake
There is also a cmake configuration you can use to build instead of using the `./build` script. While `./build` depends on Bash and is geared toward POSIX systems, cmake is platform-independent. You should use your platform's cmake tools to continue. On POSIX systems, you can do this:
cmake . && make
To get a list of options supported by the cmake configuration, use:
cmake -LH
Options can be toggled like so:
cmake -DUSE_COLOR=OFF -DBUILD_MPW_TESTS=ON . && make
## Details
The build script comes with a default configuration which can be adjusted. Full details on the build script are available by opening the build script file.
[targets='...'] [mpw_feature=0|1 ...] [CFLAGS='...'] [LDFLAGS='...'] ./build [cc arguments ...]
By default, the build script only builds the `mpw` target. You can specify other targets or `all` to build all available targets. These are the currently available targets:
- `mpw` : The main app. It needs: `mpw_sodium`, optionally supports: `mpw_color`, `mpw_json`.
- `mpw-bench` : A benchmark utility. It needs: `mpw_sodium`.
- `mpw-tests` : An algorithm test suite. It needs: `mpw_sodium`, `mpw_xml`.
It is smart to build the test suite along with the app, eg.:
targets='mpw mpw-tests' ./build
The needed and supported features determine the dependencies that the build will require. The following features exist:
- `mpw_sodium` : Use Sodium for the crypto implementation. It needs libsodium.
- `mpw_json` : Support JSON-based user configuration format. It needs libjson-c.
- `mpw_color` : Show a colorized identicon. It needs libncurses.
- `mpw_xml` : Support XML parsing. It needs libxml2.
By default, all features are enabled. Each feature can be disabled or enabled explicitly by prefixing the build command with an assignment of it to `0` or `1`, eg.:
mpw_color=0 ./build
As a result of this command, you'd build the `mpw` target (which supports `mpw_color`) without color support. The build no longer requires `libncurses` but the resulting `mpw` binary will not have support for colorized identicons.
You can also pass CFLAGS or LDFLAGS to the build, or extra custom compiler arguments as arguments to the build script.
For instance, to add a custom library search path, you could use:
LDFLAGS='-L/usr/local/lib' ./build
## Testing
Once the client is built, you should run a test suite to make sure everything works as intended.
There are currently two test suites:
- `mpw-tests` : Tests the Master Password algorithm implementation.
- `mpw-cli-tests` : Tests the CLI application.
The `mpw-tests` suite is only available if you enabled its target during build (see "Details" above).
The `mpw-cli-tests` is a Bash shell script, hence depends on your system having Bash available.
## Installing
Once you're happy with the result, you can install the `mpw` application into your system's `PATH`.
Generally, all you need to do is copy the `mpw` file into a PATH directory, eg.:
cp mpw /usr/local/bin/
The directory that you should copy the `mpw` file into will depend on your system. Also note that `cp` is a POSIX command, if your system is not a POSIX system (eg. Windows) you'll need to adjust accordingly.
There is also an `install` script to help with this process, though it is a Bash script and therefore requires that you have Bash installed:
./install
After installing, you should be able to run `mpw` and use it from anywhere in the terminal:
mpw -h
mpw google.com

16
platform-independent/c/cli/.gitignore vendored Normal file
View File

@@ -0,0 +1,16 @@
*.o
*.dSYM
mpw
mpw-bench
mpw-tests
TAG
VERSION
mpw-*.tar.gz
mpw-*.tar.gz.sig
CMakeCache.txt
CMakeFiles
Makefile
cmake_install.cmake

View File

@@ -0,0 +1,163 @@
### CMAKE
project( mpw C )
cmake_minimum_required( VERSION 3.0.2 )
### CONFIGURATION
# Features.
option( USE_SODIUM "Implement crypto functions with sodium (depends on libsodium)." ON )
option( USE_JSON "Support JSON-based user configuration format (depends on libjson-c)." ON )
option( USE_COLOR "Colorized identicon (depends on libncurses)." ON )
option( USE_XML "XML parsing (depends on libxml2)." ON )
option( BUILD_MPW "C CLI version of Master Password (needs: mpw_sodium, optional: mpw_color, mpw_json)." ON )
option( BUILD_MPW_BENCH "C CLI Master Password benchmark utility (needs: mpw_sodium)." OFF )
option( BUILD_MPW_TESTS "C Master Password algorithm test suite (needs: mpw_sodium, mpw_xml)." OFF )
# Default build flags.
set( CMAKE_BUILD_TYPE Release )
set( CMAKE_C_FLAGS "-O3" )
# Version.
find_package( Git )
if( GIT_FOUND )
execute_process( COMMAND "${GIT_EXECUTABLE}" describe --match *-cli* --long --dirty
OUTPUT_VARIABLE mpw_version OUTPUT_STRIP_TRAILING_WHITESPACE )
endif()
if( NOT mpw_version MATCHES "." )
file( READ "VERSION" mpw_version )
string( STRIP "${mpw_version}" mpw_version )
endif()
if( mpw_version MATCHES "." )
add_definitions( "-DMP_VERSION=${mpw_version}" )
message( STATUS "Current mpw source version ${mpw_version}..." )
else()
message( STATUS "Current mpw source version unknown..." )
endif()
### DEPENDENCIES
function( use_mpw_sodium t r )
if( USE_SODIUM )
target_link_libraries( "${t}" sodium )
target_compile_definitions( "${t}" PUBLIC -DMPW_SODIUM=1 )
message( STATUS "${t}: USE_SODIUM is enabled." )
elseif( r STREQUAL "required" )
message( FATAL_ERROR "${t}: USE_SODIUM was required but is not enabled. Please enable the option or remove this target." )
else()
message( STATUS "${t}: USE_SODIUM is supported but not enabled." )
endif()
endfunction()
function( use_mpw_color t )
find_package( Curses )
if( USE_COLOR )
if ( CURSES_FOUND )
target_include_directories( "${t}" PUBLIC ${CURSES_INCLUDE_DIR} )
target_link_libraries( "${t}" ${CURSES_LIBRARIES} )
target_compile_definitions( "${t}" PUBLIC -DMPW_COLOR=1 ${CURSES_DEFINITIONS} )
message( STATUS "${t}: USE_COLOR is enabled." )
elseif( r STREQUAL "required" )
message( FATAL_ERROR "${t}: USE_COLOR was enabled but is missing libcurses. Please install this library before continuing." )
else()
message( WARNING "${t}: USE_COLOR was enabled but is missing libcurses. Will continue with USE_COLOR disabled!" )
endif()
elseif( r STREQUAL "required" )
message( FATAL_ERROR "${t}: USE_COLOR was required but is not enabled. Please enable the option or remove this target." )
else()
message( STATUS "${t}: USE_COLOR is supported but not enabled." )
endif()
endfunction()
function( use_mpw_json t )
if( USE_JSON )
target_link_libraries( "${t}" json-c )
target_compile_definitions( "${t}" PUBLIC -DMPW_JSON=1 )
message( STATUS "${t}: USE_JSON is enabled." )
elseif( r STREQUAL "required" )
message( FATAL_ERROR "${t}: USE_JSON was required but is not enabled. Please enable the option or remove this target." )
else()
message( STATUS "${t}: USE_JSON is supported but not enabled." )
endif()
endfunction()
function( use_mpw_xml t r )
find_package( LibXml2 )
if( USE_XML )
if ( LIBXML2_FOUND )
target_include_directories( "${t}" PUBLIC ${LIBXML2_INCLUDE_DIR} )
target_link_libraries( "${t}" ${LIBXML2_LIBRARIES} )
target_compile_definitions( "${t}" PUBLIC -DMPW_XML=1 ${LIBXML2_DEFINITIONS} )
message( STATUS "${t}: USE_XML is enabled." )
elseif( r STREQUAL "required" )
message( FATAL_ERROR "${t}: USE_XML was enabled but is missing libxml2. Please install this library before continuing." )
else()
message( WARNING "${t}: USE_XML was enabled but is missing libxml2. Will continue with USE_XML disabled!" )
endif()
elseif( r STREQUAL "required" )
message( FATAL_ERROR "${t}: USE_XML was required but is not enabled. Please enable the option or remove this target." )
else()
message( STATUS "${t}: USE_XML is supported but not enabled." )
endif()
endfunction()
### TARGET: MPW
if( BUILD_MPW )
# target
add_executable( mpw "../core/src/base64.c" "../core/src/aes.c" "../core/src/mpw-algorithm.c" "../core/src/mpw-types.c" "../core/src/mpw-util.c"
"../core/src/mpw-marshal-util.c" "../core/src/mpw-marshal.c"
"src/mpw-cli-util.c" "src/mpw-cli.c" )
target_include_directories( mpw PUBLIC ../core/src src )
install( TARGETS mpw RUNTIME DESTINATION bin )
# dependencies
use_mpw_sodium( mpw required )
use_mpw_color( mpw optional )
use_mpw_json( mpw optional )
endif()
### TARGET: MPW-BENCH
if( BUILD_MPW_BENCH )
# target
add_executable( mpw-bench "../core/src/base64.c" "../core/src/aes.c" "../core/src/mpw-algorithm.c" "../core/src/mpw-types.c" "../core/src/mpw-util.c"
"src/mpw-bench.c" )
target_include_directories( mpw-bench PUBLIC ../core/src src )
install( TARGETS mpw-bench RUNTIME DESTINATION bin )
# dependencies
use_mpw_sodium( mpw-bench required )
endif()
### TARGET: MPW-TESTS
if( BUILD_MPW_TESTS )
# target
add_executable( mpw-tests "../core/src/base64.c" "../core/src/aes.c" "../core/src/mpw-algorithm.c" "../core/src/mpw-types.c" "../core/src/mpw-util.c"
"src/mpw-tests-util.c" "src/mpw-tests.c" )
target_include_directories( mpw-tests PUBLIC ../core/src src )
install( TARGETS mpw-tests RUNTIME DESTINATION bin )
# dependencies
use_mpw_sodium( mpw-tests required )
use_mpw_xml( mpw-tests required )
endif()

View File

@@ -0,0 +1,9 @@
FROM ubuntu
WORKDIR /mpw/cli
ADD . /mpw
RUN apt-get update && apt -y install cmake libsodium-dev libjson-c-dev libncurses-dev libxml2-dev
RUN cmake -DBUILD_MPW_TESTS=ON . && make install
RUN mpw-tests
CMD mpw

View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# FIXME
# partials are currently readline words, but these can't be reliably compared against literal data. We need to make them literal first.. in a safe way. Currently using xargs' quote parser as a hack.
# Process literal completion options in COMPREPLY
#
# 1. Filter COMPREPLY by excluding the options that do not match the word that is being completed.
# 2. Shell-escape the COMPREPLY words so they remain syntactical words when injected into the completed command.
# 3. Add a space after the words so successful completions advance to the next word
# (we disabled this default behavior with -o nospace so we can do completions that don't want this, eg. directory names)
_comp_finish_completions() {
local partial=$(xargs <<< "${COMP_WORDS[COMP_CWORD]}") # FIXME
local word words=( "${COMPREPLY[@]}" )
COMPREPLY=()
for word in "${words[@]}"; do
( shopt -s nocasematch; [[ $word = $partial* ]] ) && COMPREPLY+=( "$(printf '%q ' "$word")" )
done
if (( ${#COMPREPLY[@]} > 1 )) && [[ $_comp_title ]]; then
printf '\n%s:' "$_comp_title"
unset _comp_title
fi
}
# Perform pathname completion.
#
# 1. Populate COMPREPLY with pathnames.
# 2. Shell-escape the COMPREPLY words so they remain syntactical words when injected into the completed command.
# 3. Add a space after file names so successful completions advance to the next word.
# Directory names are suffixed with a / instead so we can keep completing the files inside.
_comp_complete_path() {
local partial=$(xargs <<< "${COMP_WORDS[COMP_CWORD]}")
local path
COMPREPLY=()
for path in "$partial"*; do
if [[ -d $path ]]; then
COMPREPLY+=( "$(printf '%q/' "$path")" )
elif [[ -e $path ]]; then
COMPREPLY+=( "$(printf '%q ' "$path")" )
fi
done
}
_show_args() {
echo
local i=0
for arg; do
printf "arg %d: %s\n" "$((i++))" "$arg"
done
i=0
for word in "${COMP_WORDS[@]}"; do
printf "word %d: %s -> %s %s\n" "$i" "$word" "$(xargs <<< "$word")" "$( ((i == $COMP_CWORD)) && echo '<CWORD>' )"
let i++
done
}

1568
platform-independent/c/cli/bashlib Executable file

File diff suppressed because it is too large Load Diff

206
platform-independent/c/cli/build Executable file
View File

@@ -0,0 +1,206 @@
#!/usr/bin/env bash
#
# USAGE
# [targets='...'] [mpw_feature=0|1 ...] [CFLAGS='...'] [LDFLAGS='...'] ./build [cc arguments ...]
#
# By default, you should only need to run ./build
#
# You can customize the targets that are built using targets='...'. Use targets='all' to build all targets.
# By default, we only build the 'mpw' target.
# See targets_all for all possible targets as well as the features they support and require.
#
# Several features can be enabled or disabled using feature flags.
# See the Features section for an overview of the features, their default setting, their meaning and their dependencies.
# You will need to have each of the feature's dependencies installed for the build to succeed with that feature enabled.
#
# Finally, the C compiler can be tuned using CFLAGS, LDFLAGS and compiler arguments passed to the script.
#
# BUGS
# masterpassword@lyndir.com
#
# AUTHOR
# Maarten Billemont
#
cd "${BASH_SOURCE%/*}"
shopt -s extglob
set -e
### CONFIGURATION
# Targets to build.
targets_all=(
mpw # C CLI version of Master Password (needs: mpw_sodium, optional: mpw_color, mpw_json).
mpw-bench # C CLI Master Password benchmark utility (needs: mpw_sodium).
mpw-tests # C Master Password algorithm test suite (needs: mpw_sodium, mpw_xml).
)
targets_default='mpw' # Override with: targets='...' ./build
# Features.
mpw_sodium=${mpw_sodium:-1} # Implement crypto functions with sodium (depends on libsodium).
mpw_json=${mpw_json:-1} # Support JSON-based user configuration format (depends on libjson-c).
mpw_color=${mpw_color:-1} # Colorized identicon (depends on libncurses).
mpw_xml=${mpw_xml:-1} # XML parsing (depends on libxml2).
# Default build flags.
cflags=( -O3 $CFLAGS )
ldflags=( $LDFLAGS )
# Version.
if { mpw_version=$(git describe --match '*-cli*' --long --dirty) || mpw_version=$(<VERSION); } 2>/dev/null; then
cflags+=( -D"MP_VERSION=$mpw_version" )
fi
echo 2>&1 "Current mpw source version ${mpw_version:-<unknown>}..."
### TARGET: MPW
mpw() {
# dependencies
use_mpw_sodium required
use_mpw_color optional
use_mpw_json optional
# target
cflags=(
"${cflags[@]}"
# mpw paths
-I"../core/src" -I"src"
)
ldflags=(
"${ldflags[@]}"
)
# build
cc "${cflags[@]}" "$@" \
"../core/src/base64.c" "../core/src/aes.c" "../core/src/mpw-algorithm.c" "../core/src/mpw-types.c" "../core/src/mpw-util.c" \
"../core/src/mpw-marshal-util.c" "../core/src/mpw-marshal.c" "src/mpw-cli-util.c" \
"${ldflags[@]}" "src/mpw-cli.c" -o "mpw"
echo "done! You can now run ./mpw-cli-tests, ./install or use ./$_"
}
### TARGET: MPW-BENCH
mpw-bench() {
# dependencies
use_mpw_sodium required
# target
cflags=(
"${cflags[@]}"
# mpw paths
-I"../core/src" -I"src"
)
ldflags=(
"${ldflags[@]}"
)
# build
cc "${cflags[@]}" "$@" \
"../core/src/base64.c" "../core/src/aes.c" "../core/src/mpw-algorithm.c" "../core/src/mpw-types.c" "../core/src/mpw-util.c" \
"${ldflags[@]}" "src/mpw-bench.c" -o "mpw-bench"
echo "done! You can now use ./$_"
}
### TARGET: MPW-TESTS
mpw-tests() {
# dependencies
use_mpw_sodium required
use_mpw_xml required
# target
cflags=(
"${cflags[@]}"
# mpw paths
-I"../core/src" -I"src"
)
ldflags=(
"${ldflags[@]}"
)
# build
cc "${cflags[@]}" "$@" \
"../core/src/base64.c" "../core/src/aes.c" "../core/src/mpw-algorithm.c" "../core/src/mpw-types.c" "../core/src/mpw-util.c" "src/mpw-tests-util.c" \
"${ldflags[@]}" "src/mpw-tests.c" -o "mpw-tests"
echo "done! You can now use ./$_"
}
### TOOLS
haslib() {
cc -x c "${ldflags[@]}" -l"$1" -o /dev/null - <<< 'int main() { return 0; }' &>/dev/null
}
cc() {
if hash llvm-gcc 2>/dev/null; then
llvm-gcc "$@"
elif hash gcc 2>/dev/null; then
gcc -std=c11 "$@"
elif hash clang 2>/dev/null; then
clang "$@"
else
echo >&2 "Need a compiler. Please install GCC or LLVM."
exit 1
fi
}
### DEPENDENCIES
use() {
local option=$1 requisite=$2 lib=$3; shift 3
local enabled=${!option}
if (( enabled )); then
if haslib "$lib"; then
for lib in "$lib" "$@"; do
haslib "$lib" && ldflags+=( -l"$lib" )
done
echo >&2 "INFO: Enabled $option (lib$lib)."
return 0
elif [[ $requisite == required ]]; then
echo >&2 "ERROR: $option was enabled but is missing $lib library. Please install this library before continuing."
exit 1
else
echo >&2 "WARNING: $option was enabled but is missing $lib library. Will continue with $option disabled!"
return 1
fi
elif [[ $requisite == required ]]; then
echo >&2 "ERROR: $option was required but is not enabled. Please enable the option or remove this target before continuing."
exit 1
else
echo >&2 "INFO: $option is supported but not enabled."
return 1
fi
}
use_mpw_sodium() {
local requisite=$1
use mpw_sodium "$requisite" sodium && cflags+=( -D"MPW_SODIUM=1" ) ||:
}
use_mpw_color() {
local requisite=$1
use mpw_color "$requisite" curses tinfo && cflags+=( -D"MPW_COLOR=1" ) ||:
}
use_mpw_json() {
local requisite=$1
use mpw_json "$requisite" json-c && cflags+=( -D"MPW_JSON=1" ) ||:
}
use_mpw_xml() {
local requisite=$1
use mpw_xml "$requisite" xml2 && cflags+=( -D"MPW_XML=1" -I"/usr/include/libxml2" -I"/usr/local/include/libxml2" ) ||:
}
### BUILD TARGETS
for target in "${targets_all[@]}"; do
if [[ ${targets:-$targets_default} == 'all' || " ${targets:-$targets_default} " = *" $target "* ]]; then
echo
echo "Building target: $target..."
( "$target" "$@" )
fi
done

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
#
# Clean all generated build files.
set -e
cd "${BASH_SOURCE%/*}"
rm -vfr mpw mpw-bench mpw-tests

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -e
cd "${BASH_SOURCE%/*}/.."
tag=$(git describe --exact-match --match '*-cli*') || { echo >&2 "Tree is not at a release tag."; exit 1; }
version=$(git describe --match '*-cli*' --long --dirty --broken)
[[ $version != *-dirty ]] || { echo >&2 "Tree is dirty, first commit any changes."; exit 1; }
mpwArchive=mpw-$version.tar.gz
[[ -e $mpwArchive ]] && echo >&2 "WARNING: $mpwArchive already exists. Will overwrite."
read -n1 -p "Will prepare and release $mpwArchive. Press a key to continue or ^C to abort."
echo "Cleaning .."
git clean -ffdx .
echo "Creating archive $mpwArchive .."
echo "$version" > VERSION
git show --show-signature --pretty=format:%H --quiet "$tag" > TAG
{ git ls-files -z .; printf '%s\0' VERSION TAG; } | xargs -0 tar -Lcvzf "$mpwArchive"
echo "Creating archive signature $mpwArchive.sig .."
gpg --detach-sign --local-user 5C2D1D61853F20F2FCDDCCB70EF21226F43EA6BC "$mpwArchive"
echo "Installing archive and signature in site .."
cd ../../public/site
mv "$OLDPWD/$mpwArchive" .; [[ -e $_ ]]
mv "$OLDPWD/$mpwArchive.sig" .; [[ -e $_ ]]
ln -sf "$mpwArchive" "masterpassword-cli.tar.gz"; [[ -e $_ ]]
ln -sf "$mpwArchive.sig" "masterpassword-cli.tar.gz.sig"; [[ -e $_ ]]
echo
echo "Done. Ready to publish the site."
echo " package: $mpwArchive"
echo " signature: $mpwArchive.sig"
echo " url: https://masterpassword.app/$mpwArchive"

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env bash
#
# Install the Master Password CLI tool.
set -e
cd "${BASH_SOURCE%/*}"
source bashlib
inf "This will install the mpw tool."
# Try to guess then ask for the bin dir to install to.
IFS=: read -a paths <<< "$PATH"
if inArray ~/bin "${paths[@]}"; then
bindir=~/bin
elif inArray ~/.bin "${paths[@]}"; then
bindir=~/.bin
elif inArray /usr/local/bin "${paths[@]}"; then
bindir=/usr/local/bin
else
bindir=~/bin
fi
bindir=$(ask -d "$bindir" "What bin directory should I install to?")
[[ -d "$bindir" ]] || mkdir "$bindir" || ftl 'Cannot create missing bin directory: %s' "$bindir" || exit
[[ -w "$bindir" ]] || ftl 'Cannot write to bin directory: %s' "$bindir" || exit
# Install Master Password.
install -m555 mpw "$bindir"
[[ ! -e "$bindir/bashlib" ]] && install bashlib "$bindir" ||:
# Convenience bash function.
inf "Installation successful!"
echo
inf "To improve usability, you can install an mpw function in your bash shell."
inf "This function adds the following features:"
inf " - Automatically remember your user name in the shell if not set."
inf " - Automatically put the password in the clipboard (some platforms)."
echo
inf "To do this you need the following function in ~/.bashrc:\n%s" "$(<mpw.bashrc)"
echo
inf "We can do this for you automatically now."
if ask -c Y!n "Append the mpw function to your .bashrc?"; then
cat mpw.bashrc >> ~/.bashrc
inf "Done! Don't forget to run '%s' to apply the changes!" "source ~/.bashrc"
fi
echo
inf "You can also save your user name in ~/.bashrc. Leave blank to skip this step."
if MPW_FULLNAME=$(ask "Your full name:") && [[ $MPW_FULLNAME ]] ; then
printf 'export MPW_FULLNAME=%q\n' "$MPW_FULLNAME" >> ~/.bashrc
fi
inf "If you have an askpass program you'd like to use, you can specify it here."
inf "An askpass program provides a graphical interface for entering things like your master password."
inf "Leave blank to skip this step and enter passwords using the terminal."
if [[ ! $MPW_ASKPASS ]] && hash ssh-askpass 2>/dev/null; then
MPW_ASKPASS=ssh-askpass
fi
if MPW_ASKPASS=$(ask +"$MPW_ASKPASS" "askpass program:") && [[ $MPW_ASKPASS ]] ; then
printf 'export MPW_ASKPASS=%q\n' "$MPW_ASKPASS" >> ~/.bashrc
fi
echo
inf "Shell features installed."
inf "To load these convenience features into your already running shell, type: source ~/.bashrc"
inf "To begin using Master Password, type: mpw -h or mpw my-site-name"

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bash
cd "${BASH_SOURCE%/*}"
# Tooling
errors=0
mpw_expect() {
local expect=$1; shift
printf '.'
result=$(./mpw -q "$@") err=$?
if (( err )); then
printf >&2 "Error (exit %d) mpw%s\n" "$err" "$(printf ' %q' "$@")"
return $(( ++errors ))
fi
if [[ $result != $expect ]]; then
printf >&2 "Error (got: %s != expected: %s) mpw%s\n" "$result" "$expect" "$(printf ' %q' "$@")"
return $(( ++errors ))
fi
}
# mpw_tests.xml
## V3
mpw_expect 'Jejr5[RepuSosp' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' 'masterpasswordapp.com'
mpw_expect 'Jejr5[RepuSosp' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -C '' 'masterpasswordapp.com'
mpw_expect 'Jejr5[RepuSosp' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jejr5[RepuSosp' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jejr5[RepuSosp' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jejr5[RepuSosp' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'NopaDajh8=Fene' -Fnone -u '⛄' -M 'banana colored duckling' -tlong -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'QesuHirv5-Xepl' -Fnone -u 'Robert Lee Mitchell' -M '⛄' -tlong -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'LiheCuwhSerz6)' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a3 -p 'authentication' -C '' '⛄'
mpw_expect 'wohzaqage' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -p 'identification' 'masterpasswordapp.com'
mpw_expect 'wohzaqage' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tname -c1 -a3 -p 'identification' -C '' 'masterpasswordapp.com'
mpw_expect 'xin diyjiqoja hubu' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -p 'recovery' 'masterpasswordapp.com'
mpw_expect 'xin diyjiqoja hubu' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a3 -p 'recovery' -C '' 'masterpasswordapp.com'
mpw_expect 'xogx tem cegyiva jab' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a3 -p 'recovery' -C 'question' 'masterpasswordapp.com'
mpw_expect 'W6@692^B1#&@gVdSdLZ@' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tmax -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jej2$Quv' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tmed -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'WAo2xIg6' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tbasic -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jej2' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tshort -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect '7662' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tpin -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'jejraquvo' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tname -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'jejr quv cabsibu tam' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a3 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'XambHoqo6[Peni' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c4294967295 -a3 -p 'authentication' 'masterpasswordapp.com'
## V2
mpw_expect 'Jejr5[RepuSosp' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'WaqoGuho2[Xaxw' -Fnone -u '⛄' -M 'banana colored duckling' -tlong -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'QesuHirv5-Xepl' -Fnone -u 'Robert Lee Mitchell' -M '⛄' -tlong -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'LiheCuwhSerz6)' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a2 -p 'authentication' -C '' '⛄'
mpw_expect 'wohzaqage' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tname -c1 -a2 -p 'identification' -C '' 'masterpasswordapp.com'
mpw_expect 'xin diyjiqoja hubu' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a2 -p 'recovery' -C '' 'masterpasswordapp.com'
mpw_expect 'xogx tem cegyiva jab' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a2 -p 'recovery' -C 'question' 'masterpasswordapp.com'
mpw_expect 'W6@692^B1#&@gVdSdLZ@' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tmax -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jej2$Quv' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tmed -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'WAo2xIg6' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tbasic -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jej2' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tshort -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect '7662' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tpin -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'jejraquvo' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tname -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'jejr quv cabsibu tam' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a2 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'XambHoqo6[Peni' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c4294967295 -a2 -p 'authentication' 'masterpasswordapp.com'
## V1
mpw_expect 'Jejr5[RepuSosp' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'WaqoGuho2[Xaxw' -Fnone -u '⛄' -M 'banana colored duckling' -tlong -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'QesuHirv5-Xepl' -Fnone -u 'Robert Lee Mitchell' -M '⛄' -tlong -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'WawiYarp2@Kodh' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a1 -p 'authentication' -C '' '⛄'
mpw_expect 'wohzaqage' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tname -c1 -a1 -p 'identification' -C '' 'masterpasswordapp.com'
mpw_expect 'xin diyjiqoja hubu' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a1 -p 'recovery' -C '' 'masterpasswordapp.com'
mpw_expect 'xogx tem cegyiva jab' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a1 -p 'recovery' -C 'question' 'masterpasswordapp.com'
mpw_expect 'W6@692^B1#&@gVdSdLZ@' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tmax -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jej2$Quv' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tmed -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'WAo2xIg6' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tbasic -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Jej2' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tshort -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect '7662' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tpin -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'jejraquvo' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tname -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'jejr quv cabsibu tam' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a1 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'XambHoqo6[Peni' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c4294967295 -a1 -p 'authentication' 'masterpasswordapp.com'
## V0
mpw_expect 'Feji5@ReduWosh' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'HajrYudo7@Mamh' -Fnone -u '⛄' -M 'banana colored duckling' -tlong -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'MewmDini0]Meho' -Fnone -u 'Robert Lee Mitchell' -M '⛄' -tlong -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'HahiVana2@Nole' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c1 -a0 -p 'authentication' -C '' '⛄'
mpw_expect 'lozwajave' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tname -c1 -a0 -p 'identification' -C '' 'masterpasswordapp.com'
mpw_expect 'miy lirfijoja dubu' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a0 -p 'recovery' -C '' 'masterpasswordapp.com'
mpw_expect 'movm bex gevrica jaf' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a0 -p 'recovery' -C 'question' 'masterpasswordapp.com'
mpw_expect 'w1!3bA3icmRAc)SS@lwl' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tmax -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Fej7]Jug' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tmed -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'wvH7irC1' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tbasic -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'Fej7' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tshort -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect '2117' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tpin -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'fejrajugo' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tname -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'fejr jug gabsibu bax' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tphrase -c1 -a0 -p 'authentication' -C '' 'masterpasswordapp.com'
mpw_expect 'QateDojh1@Hecn' -Fnone -u 'Robert Lee Mitchell' -M 'banana colored duckling' -tlong -c4294967295 -a0 -p 'authentication' 'masterpasswordapp.com'
# Finish
printf 'Done!\n'
exit "$errors"

View File

@@ -0,0 +1,24 @@
## Added by Master Password
source bashlib
mpw() {
_copy() {
if hash pbcopy 2>/dev/null; then
pbcopy
elif hash xclip 2>/dev/null; then
xclip -selection clip
else
cat; echo 2>/dev/null
return
fi
echo >&2 "Copied!"
}
# Empty the clipboard
:| _copy 2>/dev/null
# Ask for the user's name and password if not yet known.
MPW_FULLNAME=${MPW_FULLNAME:-$(ask 'Your Full Name:')}
# Start Master Password and copy the output.
printf %s "$(MPW_FULLNAME=$MPW_FULLNAME command mpw "$@")" | _copy
}

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bash
source bashcomplib
# completing the 'mpw' command.
_comp_mpw() {
local optarg= cword=${COMP_WORDS[COMP_CWORD]} pcword=
(( COMP_CWORD )) && pcword=${COMP_WORDS[COMP_CWORD - 1]}
case $pcword in
-u) # complete full names.
COMPREPLY=( ~/.mpw.d/*.mpsites )
[[ -e $COMPREPLY ]] || COMPREPLY=()
COMPREPLY=( "${COMPREPLY[@]##*/}" ) COMPREPLY=( "${COMPREPLY[@]%.mpsites}" )
;;
-t) # complete types.
COMPREPLY=( maximum long medium basic short pin name phrase )
;;
-c) # complete counter.
COMPREPLY=( 1 )
;;
-V) # complete versions.
COMPREPLY=( 0 1 2 3 )
;;
-v) # complete variants.
COMPREPLY=( password login answer )
;;
-C) # complete context.
;;
*)
# previous word is not an option we can complete, complete site name (or option if leading -)
if [[ $cword = -* ]]; then
COMPREPLY=( -u -t -c -V -v -C )
else
local w fullName=$MP_FULLNAME
for (( w = 0; w < ${#COMP_WORDS[@]}; ++w )); do
[[ ${COMP_WORDS[w]} = -u ]] && fullName=$(xargs <<< "${COMP_WORDS[w + 1]}") && break
done
if [[ -e ~/.mpw.d/"$fullName.mpsites" ]]; then
IFS=$'\n' read -d '' -ra COMPREPLY < <(awk -F$'\t' '!/^ *#/{sub(/^ */, "", $2); print $2}' ~/.mpw.d/"$fullName.mpsites")
printf -v _comp_title 'Sites for %s' "$fullName"
else
# Default list from the Alexa Top 500
COMPREPLY=(
163.com 360.cn 9gag.com adobe.com alibaba.com aliexpress.com amazon.com
apple.com archive.org ask.com baidu.com battle.net booking.com buzzfeed.com
chase.com cnn.com comcast.net craigslist.org dailymotion.com dell.com
deviantart.com diply.com disqus.com dropbox.com ebay.com engadget.com
espn.go.com evernote.com facebook.com fedex.com feedly.com flickr.com
flipkart.com github.com gizmodo.com go.com goodreads.com google.com
huffingtonpost.com hulu.com ign.com ikea.com imdb.com imgur.com
indiatimes.com instagram.com jd.com kickass.to kickstarter.com linkedin.com
live.com livedoor.com mail.ru mozilla.org naver.com netflix.com newegg.com
nicovideo.jp nytimes.com pandora.com paypal.com pinterest.com pornhub.com
qq.com rakuten.co.jp reddit.com redtube.com shutterstock.com skype.com
soso.com spiegel.de spotify.com stackexchange.com steampowered.com
stumbleupon.com taobao.com target.com thepiratebay.se tmall.com
torrentz.eu tripadvisor.com tube8.com tubecup.com tudou.com tumblr.com
twitter.com uol.com.br vimeo.com vk.com walmart.com weibo.com whatsapp.com
wikia.com wikipedia.org wired.com wordpress.com xhamster.com xinhuanet.com
xvideos.com yahoo.com yandex.ru yelp.com youku.com youporn.com ziddu.com
)
fi
fi ;;
esac
_comp_finish_completions
}
#complete -F _show_args mpw
complete -o nospace -F _comp_mpw mpw

View File

@@ -0,0 +1,279 @@
<tests>
<!-- Default values for all parameters. -->
<case id="default">
<algorithm>-1</algorithm>
<fullName>Robert Lee Mitchell</fullName>
<masterPassword>banana colored duckling</masterPassword>
<keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
<siteName>masterpasswordapp.com</siteName>
<siteCounter>1</siteCounter>
<resultType>Long</resultType>
<keyPurpose>Authentication</keyPurpose>
<result><!-- abstract --></result>
</case>
<!-- Algorithm 3 -->
<case id="v3" parent="default">
<algorithm>3</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v3_mb_fullName" parent="v3">
<fullName></fullName>
<keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
<result>NopaDajh8=Fene</result>
</case>
<case id="v3_mb_masterPassword" parent="v3">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v3_mb_siteName" parent="v3">
<siteName></siteName>
<result>LiheCuwhSerz6)</result>
</case>
<case id="v3_loginName" parent="v3">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>wohzaqage</result>
</case>
<case id="v3_securityAnswer" parent="v3">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v3_securityAnswer_context" parent="v3_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v3_type_maximum" parent="v3">
<resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v3_type_medium" parent="v3">
<resultType>Medium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v3_type_basic" parent="v3">
<resultType>Basic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v3_type_short" parent="v3">
<resultType>Short</resultType>
<result>Jej2</result>
</case>
<case id="v3_type_pin" parent="v3">
<resultType>PIN</resultType>
<result>7662</result>
</case>
<case id="v3_type_name" parent="v3">
<resultType>Name</resultType>
<result>jejraquvo</result>
</case>
<case id="v3_type_phrase" parent="v3">
<resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v3_counter_ceiling" parent="v3">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 2 -->
<case id="v2" parent="default">
<algorithm>2</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v2_mb_fullName" parent="v2">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v2_mb_masterPassword" parent="v2">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v2_mb_siteName" parent="v2">
<siteName></siteName>
<result>LiheCuwhSerz6)</result>
</case>
<case id="v2_loginName" parent="v2">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>wohzaqage</result>
</case>
<case id="v2_securityAnswer" parent="v2">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v2_securityAnswer_context" parent="v2_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v2_type_maximum" parent="v2">
<resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v2_type_medium" parent="v2">
<resultType>Medium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v2_type_basic" parent="v2">
<resultType>Basic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v2_type_short" parent="v2">
<resultType>Short</resultType>
<result>Jej2</result>
</case>
<case id="v2_type_pin" parent="v2">
<resultType>PIN</resultType>
<result>7662</result>
</case>
<case id="v2_type_name" parent="v2">
<resultType>Name</resultType>
<result>jejraquvo</result>
</case>
<case id="v2_type_phrase" parent="v2">
<resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v2_counter_ceiling" parent="v2">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 1 -->
<case id="v1" parent="default">
<algorithm>1</algorithm>
<result>Jejr5[RepuSosp</result>
</case>
<case id="v1_mb_fullName" parent="v1">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<result>WaqoGuho2[Xaxw</result>
</case>
<case id="v1_mb_masterPassword" parent="v1">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>QesuHirv5-Xepl</result>
</case>
<case id="v1_mb_siteName" parent="v1">
<siteName></siteName>
<result>WawiYarp2@Kodh</result>
</case>
<case id="v1_loginName" parent="v1">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>wohzaqage</result>
</case>
<case id="v1_securityAnswer" parent="v1">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>xin diyjiqoja hubu</result>
</case>
<case id="v1_securityAnswer_context" parent="v1_securityAnswer">
<keyContext>question</keyContext>
<result>xogx tem cegyiva jab</result>
</case>
<case id="v1_type_maximum" parent="v1">
<resultType>Maximum</resultType>
<result>W6@692^B1#&amp;@gVdSdLZ@</result>
</case>
<case id="v1_type_medium" parent="v1">
<resultType>Medium</resultType>
<result>Jej2$Quv</result>
</case>
<case id="v1_type_basic" parent="v1">
<resultType>Basic</resultType>
<result>WAo2xIg6</result>
</case>
<case id="v1_type_short" parent="v1">
<resultType>Short</resultType>
<result>Jej2</result>
</case>
<case id="v1_type_pin" parent="v1">
<resultType>PIN</resultType>
<result>7662</result>
</case>
<case id="v1_type_name" parent="v1">
<resultType>Name</resultType>
<result>jejraquvo</result>
</case>
<case id="v1_type_phrase" parent="v1">
<resultType>Phrase</resultType>
<result>jejr quv cabsibu tam</result>
</case>
<case id="v1_counter_ceiling" parent="v1">
<siteCounter>4294967295</siteCounter>
<result>XambHoqo6[Peni</result>
</case>
<!-- Algorithm 0 -->
<case id="v0" parent="default">
<algorithm>0</algorithm>
<result>Feji5@ReduWosh</result>
</case>
<case id="v0_mb_fullName" parent="v0">
<fullName></fullName>
<keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
<result>HajrYudo7@Mamh</result>
</case>
<case id="v0_mb_masterPassword" parent="v0">
<masterPassword></masterPassword>
<keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
<result>MewmDini0]Meho</result>
</case>
<case id="v0_mb_siteName" parent="v0">
<siteName></siteName>
<result>HahiVana2@Nole</result>
</case>
<case id="v0_loginName" parent="v0">
<keyPurpose>Identification</keyPurpose>
<resultType>Name</resultType>
<result>lozwajave</result>
</case>
<case id="v0_securityAnswer" parent="v0">
<keyPurpose>Recovery</keyPurpose>
<resultType>Phrase</resultType>
<result>miy lirfijoja dubu</result>
</case>
<case id="v0_securityAnswer_context" parent="v0_securityAnswer">
<keyContext>question</keyContext>
<result>movm bex gevrica jaf</result>
</case>
<case id="v0_type_maximum" parent="v0">
<resultType>Maximum</resultType>
<result>w1!3bA3icmRAc)SS@lwl</result>
</case>
<case id="v0_type_medium" parent="v0">
<resultType>Medium</resultType>
<result>Fej7]Jug</result>
</case>
<case id="v0_type_basic" parent="v0">
<resultType>Basic</resultType>
<result>wvH7irC1</result>
</case>
<case id="v0_type_short" parent="v0">
<resultType>Short</resultType>
<result>Fej7</result>
</case>
<case id="v0_type_pin" parent="v0">
<resultType>PIN</resultType>
<result>2117</result>
</case>
<case id="v0_type_name" parent="v0">
<resultType>Name</resultType>
<result>fejrajugo</result>
</case>
<case id="v0_type_phrase" parent="v0">
<resultType>Phrase</resultType>
<result>fejr jug gabsibu bax</result>
</case>
<case id="v0_counter_ceiling" parent="v0">
<siteCounter>4294967295</siteCounter>
<result>QateDojh1@Hecn</result>
</case>
</tests>

View File

@@ -0,0 +1,324 @@
/* $OpenBSD: bcrypt.c,v 1.57 2016/08/26 08:25:02 guenther Exp $ */
/*
* Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
* Copyright (c) 1997 Niels Provos <provos@umich.edu>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* This password hashing algorithm was designed by David Mazieres
* <dm@lcs.mit.edu> and works as follows:
*
* 1. state := InitState ()
* 2. state := ExpandKey (state, salt, password)
* 3. REPEAT rounds:
* state := ExpandKey (state, 0, password)
* state := ExpandKey (state, 0, salt)
* 4. ctext := "OrpheanBeholderScryDoubt"
* 5. REPEAT 64:
* ctext := Encrypt_ECB (state, ctext);
* 6. RETURN Concatenate (salt, ctext);
*
*/
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "blf.h"
#include "blowfish.h"
#include "mpw-util.h"
/* This implementation is adaptable to current computing power.
* You can have up to 2^31 rounds which should be enough for some
* time to come.
*/
#define BCRYPT_VERSION '2'
#define BCRYPT_MAXSALT 16 /* Precomputation is just so nice */
#define BCRYPT_WORDS 6 /* Ciphertext words */
#define BCRYPT_MINLOGROUNDS 4 /* we have log2(rounds) in salt */
#define BCRYPT_SALTSPACE (7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1)
#define BCRYPT_HASHSPACE 61
static int encode_base64(char *, const uint8_t *, size_t);
static int decode_base64(uint8_t *, size_t, const char *);
/*
* Generates a salt for this version of crypt.
*/
static int
bcrypt_initsalt(int log_rounds, uint8_t *salt, size_t saltbuflen) {
uint8_t csalt[BCRYPT_MAXSALT];
if (saltbuflen < BCRYPT_SALTSPACE) {
errno = EINVAL;
return -1;
}
arc4random_buf( csalt, sizeof( csalt ) );
if (log_rounds < 4)
log_rounds = 4;
else if (log_rounds > 31)
log_rounds = 31;
snprintf( (char *)salt, saltbuflen, "$2b$%2.2u$", log_rounds );
encode_base64( (char *)salt + 7, csalt, sizeof( csalt ) );
return 0;
}
/*
* the core bcrypt function
*/
static int
bcrypt_hashpass(const char *key, const uint8_t *salt, char *encrypted,
size_t encryptedlen) {
blf_ctx state;
uint32_t rounds, i, k;
uint16_t j;
size_t key_len;
uint8_t salt_len, logr, minor;
uint8_t ciphertext[4 * BCRYPT_WORDS] = "OrpheanBeholderScryDoubt";
uint8_t csalt[BCRYPT_MAXSALT];
uint32_t cdata[BCRYPT_WORDS];
if (encryptedlen < BCRYPT_HASHSPACE)
goto inval;
/* Check and discard "$" identifier */
if (salt[0] != '$')
goto inval;
salt += 1;
if (salt[0] != BCRYPT_VERSION)
goto inval;
/* Check for minor versions */
switch ((minor = salt[1])) {
case 'a':
key_len = (uint8_t)(strlen( key ) + 1);
break;
case 'b':
/* strlen() returns a size_t, but the function calls
* below result in implicit casts to a narrower integer
* type, so cap key_len at the actual maximum supported
* length here to avoid integer wraparound */
key_len = strlen( key );
if (key_len > 72)
key_len = 72;
key_len++; /* include the NUL */
break;
default:
goto inval;
}
if (salt[2] != '$')
goto inval;
/* Discard version + "$" identifier */
salt += 3;
/* Check and parse num rounds */
if (!isdigit( (unsigned char)salt[0] ) ||
!isdigit( (unsigned char)salt[1] ) || salt[2] != '$')
goto inval;
logr = (uint8_t)((salt[1] - '0') + ((salt[0] - '0') * 10));
if (logr < BCRYPT_MINLOGROUNDS || logr > 31)
goto inval;
/* Computer power doesn't increase linearly, 2^x should be fine */
rounds = 1U << logr;
/* Discard num rounds + "$" identifier */
salt += 3;
if (strlen( (char *)salt ) * 3 / 4 < BCRYPT_MAXSALT)
goto inval;
/* We dont want the base64 salt but the raw data */
if (decode_base64( csalt, BCRYPT_MAXSALT, (char *)salt ))
goto inval;
salt_len = BCRYPT_MAXSALT;
/* Setting up S-Boxes and Subkeys */
Blowfish_initstate( &state );
Blowfish_expandstate( &state, csalt, salt_len,
(uint8_t *)key, (uint16_t)key_len );
for (k = 0; k < rounds; k++) {
Blowfish_expand0state( &state, (uint8_t *)key, (uint16_t)key_len );
Blowfish_expand0state( &state, csalt, salt_len );
}
/* This can be precomputed later */
j = 0;
for (i = 0; i < BCRYPT_WORDS; i++)
cdata[i] = Blowfish_stream2word( ciphertext, 4 * BCRYPT_WORDS, &j );
/* Now do the encryption */
for (k = 0; k < 64; k++)
blf_enc( &state, cdata, BCRYPT_WORDS / 2 );
for (i = 0; i < BCRYPT_WORDS; i++) {
ciphertext[4 * i + 3] = (uint8_t)(cdata[i] & 0xff);
cdata[i] = cdata[i] >> 8;
ciphertext[4 * i + 2] = (uint8_t)(cdata[i] & 0xff);
cdata[i] = cdata[i] >> 8;
ciphertext[4 * i + 1] = (uint8_t)(cdata[i] & 0xff);
cdata[i] = cdata[i] >> 8;
ciphertext[4 * i + 0] = (uint8_t)(cdata[i] & 0xff);
}
snprintf( encrypted, 8, "$2%c$%2.2u$", minor, logr );
encode_base64( encrypted + 7, csalt, BCRYPT_MAXSALT );
encode_base64( encrypted + 7 + 22, ciphertext, 4 * BCRYPT_WORDS - 1 );
mpw_zero( &state, sizeof state );
mpw_zero( ciphertext, sizeof ciphertext );
mpw_zero( csalt, sizeof csalt );
mpw_zero( cdata, sizeof cdata );
return 0;
inval:
errno = EINVAL;
return -1;
}
/*
* internal utilities
*/
static const uint8_t Base64Code[] =
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
static const uint8_t index_64[128] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 0, 1, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63, 255, 255,
255, 255, 255, 255, 255, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
255, 255, 255, 255, 255, 255, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, 255, 255, 255, 255, 255
};
#define CHAR64(c) ( (c) > 127 ? (uint8_t)255 : index_64[(c)])
/*
* read buflen (after decoding) bytes of data from b64data
*/
static int
decode_base64(uint8_t *buffer, size_t len, const char *b64data) {
uint8_t *bp = buffer;
const uint8_t *p = (uint8_t *)b64data;
uint8_t c1, c2, c3, c4;
while (bp < buffer + len) {
c1 = CHAR64( *p );
/* Invalid data */
if (c1 == 255)
return -1;
c2 = CHAR64( *(p + 1) );
if (c2 == 255)
return -1;
*bp++ = (uint8_t)((c1 << 2) | ((c2 & 0x30) >> 4));
if (bp >= buffer + len)
break;
c3 = CHAR64( *(p + 2) );
if (c3 == 255)
return -1;
*bp++ = (uint8_t)(((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2));
if (bp >= buffer + len)
break;
c4 = CHAR64( *(p + 3) );
if (c4 == 255)
return -1;
*bp++ = (uint8_t)(((c3 & 0x03) << 6) | c4);
p += 4;
}
return 0;
}
/*
* Turn len bytes of data into base64 encoded data.
* This works without = padding.
*/
static int
encode_base64(char *b64buffer, const uint8_t *data, size_t len) {
uint8_t *bp = (uint8_t *)b64buffer;
const uint8_t *p = data;
uint8_t c1, c2;
while (p < data + len) {
c1 = *p++;
*bp++ = Base64Code[(c1 >> 2)];
c1 = (uint8_t)((c1 & 0x03) << 4);
if (p >= data + len) {
*bp++ = Base64Code[c1];
break;
}
c2 = *p++;
c1 |= (c2 >> 4) & 0x0f;
*bp++ = Base64Code[c1];
c1 = (uint8_t)((c2 & 0x0f) << 2);
if (p >= data + len) {
*bp++ = Base64Code[c1];
break;
}
c2 = *p++;
c1 |= (c2 >> 6) & 0x03;
*bp++ = Base64Code[c1];
*bp++ = Base64Code[c2 & 0x3f];
}
*bp = '\0';
return 0;
}
/*
* classic interface
*/
static uint8_t *
bcrypt_gensalt(uint8_t log_rounds) {
static uint8_t gsalt[BCRYPT_SALTSPACE];
bcrypt_initsalt( log_rounds, gsalt, sizeof( gsalt ) );
return gsalt;
}
static char *
bcrypt(const char *pass, const uint8_t *salt) {
static char gencrypted[BCRYPT_HASHSPACE];
if (bcrypt_hashpass( pass, salt, gencrypted, sizeof( gencrypted ) ) != 0)
return NULL;
return gencrypted;
}

View File

@@ -0,0 +1,83 @@
/* $OpenBSD: blf.h,v 1.7 2007/03/14 17:59:41 grunk Exp $ */
/*
* Blowfish - a fast block cipher designed by Bruce Schneier
*
* Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#ifndef _BLF_H_
#define _BLF_H_
/* Schneier specifies a maximum key length of 56 bytes.
* This ensures that every key bit affects every cipher
* bit. However, the subkeys can hold up to 72 bytes.
* Warning: For normal blowfish encryption only 56 bytes
* of the key affect all cipherbits.
*/
#define BLF_N 16 /* Number of Subkeys */
#define BLF_MAXKEYLEN ((BLF_N-2)*4) /* 448 bits */
#define BLF_MAXUTILIZED ((BLF_N+2)*4) /* 576 bits */
/* Blowfish context */
typedef struct BlowfishContext {
uint32_t S[4][256]; /* S-Boxes */
uint32_t P[BLF_N + 2]; /* Subkeys */
} blf_ctx;
/* Raw access to customized Blowfish
* blf_key is just:
* Blowfish_initstate( state )
* Blowfish_expand0state( state, key, keylen )
*/
void Blowfish_encipher(blf_ctx *, uint32_t *, uint32_t *);
void Blowfish_decipher(blf_ctx *, uint32_t *, uint32_t *);
void Blowfish_initstate(blf_ctx *);
void Blowfish_expand0state(blf_ctx *, const uint8_t *, u_int16_t);
void Blowfish_expandstate(blf_ctx *, const uint8_t *, u_int16_t, const uint8_t *, u_int16_t);
/* Standard Blowfish */
void blf_key(blf_ctx *, const uint8_t *, u_int16_t);
void blf_enc(blf_ctx *, uint32_t *, u_int16_t);
void blf_dec(blf_ctx *, uint32_t *, u_int16_t);
void blf_ecb_encrypt(blf_ctx *, uint8_t *, uint32_t);
void blf_ecb_decrypt(blf_ctx *, uint8_t *, uint32_t);
void blf_cbc_encrypt(blf_ctx *, uint8_t *, uint8_t *, uint32_t);
void blf_cbc_decrypt(blf_ctx *, uint8_t *, uint8_t *, uint32_t);
/* Converts uint8_t to uint32_t */
uint32_t Blowfish_stream2word(const uint8_t *, u_int16_t, u_int16_t *);
#endif

View File

@@ -0,0 +1,640 @@
/* $OpenBSD: blowfish.c,v 1.19 2015/09/11 09:18:27 guenther Exp $ */
/*
* Blowfish block cipher for OpenBSD
* Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
* All rights reserved.
*
* Implementation advice by David Mazieres <dm@lcs.mit.edu>.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This code is derived from section 14.3 and the given source
* in section V of Applied Cryptography, second edition.
* Blowfish is an unpatented fast block cipher designed by
* Bruce Schneier.
*/
#if 0
#include <stdio.h> /* used for debugging */
#include <string.h>
#endif
#include "blf.h"
#undef inline
#ifdef __GNUC__
#define inline __inline
#else /* !__GNUC__ */
#define inline
#endif /* !__GNUC__ */
/* Function for Feistel Networks */
#define F(s, x) ((((s)[ (((x)>>24)&0xFF)] \
+ (s)[0x100 + (((x)>>16)&0xFF)]) \
^ (s)[0x200 + (((x)>> 8)&0xFF)]) \
+ (s)[0x300 + ( (x) &0xFF)])
#define BLFRND(s,p,i,j,n) (i ^= F(s,j) ^ (p)[n])
void
Blowfish_encipher(blf_ctx *c, uint32_t *xl, uint32_t *xr)
{
uint32_t Xl;
uint32_t Xr;
uint32_t *s = c->S[0];
uint32_t *p = c->P;
Xl = *xl;
Xr = *xr;
Xl ^= p[0];
BLFRND(s, p, Xr, Xl, 1); BLFRND(s, p, Xl, Xr, 2);
BLFRND(s, p, Xr, Xl, 3); BLFRND(s, p, Xl, Xr, 4);
BLFRND(s, p, Xr, Xl, 5); BLFRND(s, p, Xl, Xr, 6);
BLFRND(s, p, Xr, Xl, 7); BLFRND(s, p, Xl, Xr, 8);
BLFRND(s, p, Xr, Xl, 9); BLFRND(s, p, Xl, Xr, 10);
BLFRND(s, p, Xr, Xl, 11); BLFRND(s, p, Xl, Xr, 12);
BLFRND(s, p, Xr, Xl, 13); BLFRND(s, p, Xl, Xr, 14);
BLFRND(s, p, Xr, Xl, 15); BLFRND(s, p, Xl, Xr, 16);
*xl = Xr ^ p[17];
*xr = Xl;
}
void
Blowfish_decipher(blf_ctx *c, uint32_t *xl, uint32_t *xr)
{
uint32_t Xl;
uint32_t Xr;
uint32_t *s = c->S[0];
uint32_t *p = c->P;
Xl = *xl;
Xr = *xr;
Xl ^= p[17];
BLFRND(s, p, Xr, Xl, 16); BLFRND(s, p, Xl, Xr, 15);
BLFRND(s, p, Xr, Xl, 14); BLFRND(s, p, Xl, Xr, 13);
BLFRND(s, p, Xr, Xl, 12); BLFRND(s, p, Xl, Xr, 11);
BLFRND(s, p, Xr, Xl, 10); BLFRND(s, p, Xl, Xr, 9);
BLFRND(s, p, Xr, Xl, 8); BLFRND(s, p, Xl, Xr, 7);
BLFRND(s, p, Xr, Xl, 6); BLFRND(s, p, Xl, Xr, 5);
BLFRND(s, p, Xr, Xl, 4); BLFRND(s, p, Xl, Xr, 3);
BLFRND(s, p, Xr, Xl, 2); BLFRND(s, p, Xl, Xr, 1);
*xl = Xr ^ p[0];
*xr = Xl;
}
void
Blowfish_initstate(blf_ctx *c)
{
/* P-box and S-box tables initialized with digits of Pi */
static const blf_ctx initstate =
{ {
{
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a},
{
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7},
{
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0},
{
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6}
},
{
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
0x9216d5d9, 0x8979fb1b
} };
*c = initstate;
}
uint32_t
Blowfish_stream2word(const uint8_t *data, uint16_t databytes,
uint16_t *current)
{
uint8_t i;
uint16_t j;
uint32_t temp;
temp = 0x00000000;
j = *current;
for (i = 0; i < 4; i++, j++) {
if (j >= databytes)
j = 0;
temp = (temp << 8) | data[j];
}
*current = j;
return temp;
}
void
Blowfish_expand0state(blf_ctx *c, const uint8_t *key, uint16_t keybytes)
{
uint16_t i;
uint16_t j;
uint16_t k;
uint32_t temp;
uint32_t datal;
uint32_t datar;
j = 0;
for (i = 0; i < BLF_N + 2; i++) {
/* Extract 4 int8 to 1 int32 from keystream */
temp = Blowfish_stream2word(key, keybytes, &j);
c->P[i] = c->P[i] ^ temp;
}
j = 0;
datal = 0x00000000;
datar = 0x00000000;
for (i = 0; i < BLF_N + 2; i += 2) {
Blowfish_encipher(c, &datal, &datar);
c->P[i] = datal;
c->P[i + 1] = datar;
}
for (i = 0; i < 4; i++) {
for (k = 0; k < 256; k += 2) {
Blowfish_encipher(c, &datal, &datar);
c->S[i][k] = datal;
c->S[i][k + 1] = datar;
}
}
}
void
Blowfish_expandstate(blf_ctx *c, const uint8_t *data, uint16_t databytes,
const uint8_t *key, uint16_t keybytes)
{
uint16_t i;
uint16_t j;
uint16_t k;
uint32_t temp;
uint32_t datal;
uint32_t datar;
j = 0;
for (i = 0; i < BLF_N + 2; i++) {
/* Extract 4 int8 to 1 int32 from keystream */
temp = Blowfish_stream2word(key, keybytes, &j);
c->P[i] = c->P[i] ^ temp;
}
j = 0;
datal = 0x00000000;
datar = 0x00000000;
for (i = 0; i < BLF_N + 2; i += 2) {
datal ^= Blowfish_stream2word(data, databytes, &j);
datar ^= Blowfish_stream2word(data, databytes, &j);
Blowfish_encipher(c, &datal, &datar);
c->P[i] = datal;
c->P[i + 1] = datar;
}
for (i = 0; i < 4; i++) {
for (k = 0; k < 256; k += 2) {
datal ^= Blowfish_stream2word(data, databytes, &j);
datar ^= Blowfish_stream2word(data, databytes, &j);
Blowfish_encipher(c, &datal, &datar);
c->S[i][k] = datal;
c->S[i][k + 1] = datar;
}
}
}
void
__unused blf_key(blf_ctx *c, const uint8_t *k, uint16_t len)
{
/* Initialize S-boxes and subkeys with Pi */
Blowfish_initstate(c);
/* Transform S-boxes and subkeys with key */
Blowfish_expand0state(c, k, len);
}
void
blf_enc(blf_ctx *c, uint32_t *data, uint16_t blocks)
{
uint32_t *d;
uint16_t i;
d = data;
for (i = 0; i < blocks; i++) {
Blowfish_encipher(c, d, d + 1);
d += 2;
}
}
void
__unused blf_dec(blf_ctx *c, uint32_t *data, uint16_t blocks)
{
uint32_t *d;
uint16_t i;
d = data;
for (i = 0; i < blocks; i++) {
Blowfish_decipher(c, d, d + 1);
d += 2;
}
}
void
__unused blf_ecb_encrypt(blf_ctx *c, uint8_t *data, uint32_t len)
{
uint32_t l, r;
uint32_t i;
for (i = 0; i < len; i += 8) {
l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
Blowfish_encipher(c, &l, &r);
data[0] = (uint8_t)(l >> 24 & 0xff);
data[1] = (uint8_t)(l >> 16 & 0xff);
data[2] = (uint8_t)(l >> 8 & 0xff);
data[3] = (uint8_t)(l & 0xff);
data[4] = (uint8_t)(r >> 24 & 0xff);
data[5] = (uint8_t)(r >> 16 & 0xff);
data[6] = (uint8_t)(r >> 8 & 0xff);
data[7] = (uint8_t)(r & 0xff);
data += 8;
}
}
void
__unused blf_ecb_decrypt(blf_ctx *c, uint8_t *data, uint32_t len)
{
uint32_t l, r;
uint32_t i;
for (i = 0; i < len; i += 8) {
l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
Blowfish_decipher(c, &l, &r);
data[0] = (uint8_t)(l >> 24 & 0xff);
data[1] = (uint8_t)(l >> 16 & 0xff);
data[2] = (uint8_t)(l >> 8 & 0xff);
data[3] = (uint8_t)(l & 0xff);
data[4] = (uint8_t)(r >> 24 & 0xff);
data[5] = (uint8_t)(r >> 16 & 0xff);
data[6] = (uint8_t)(r >> 8 & 0xff);
data[7] = (uint8_t)(r & 0xff);
data += 8;
}
}
void
__unused blf_cbc_encrypt(blf_ctx *c, uint8_t *iv, uint8_t *data, uint32_t len)
{
uint32_t l, r;
uint32_t i, j;
for (i = 0; i < len; i += 8) {
for (j = 0; j < 8; j++)
data[j] ^= iv[j];
l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
Blowfish_encipher(c, &l, &r);
data[0] = (uint8_t)(l >> 24 & 0xff);
data[1] = (uint8_t)(l >> 16 & 0xff);
data[2] = (uint8_t)(l >> 8 & 0xff);
data[3] = (uint8_t)(l & 0xff);
data[4] = (uint8_t)(r >> 24 & 0xff);
data[5] = (uint8_t)(r >> 16 & 0xff);
data[6] = (uint8_t)(r >> 8 & 0xff);
data[7] = (uint8_t)(r & 0xff);
iv = data;
data += 8;
}
}
void
__unused blf_cbc_decrypt(blf_ctx *c, uint8_t *iva, uint8_t *data, uint32_t len)
{
uint32_t l, r;
uint8_t *iv;
uint32_t i, j;
iv = data + len - 16;
data = data + len - 8;
for (i = len - 8; i >= 8; i -= 8) {
l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
Blowfish_decipher(c, &l, &r);
data[0] = (uint8_t)(l >> 24 & 0xff);
data[1] = (uint8_t)(l >> 16 & 0xff);
data[2] = (uint8_t)(l >> 8 & 0xff);
data[3] = (uint8_t)(l & 0xff);
data[4] = (uint8_t)(r >> 24 & 0xff);
data[5] = (uint8_t)(r >> 16 & 0xff);
data[6] = (uint8_t)(r >> 8 & 0xff);
data[7] = (uint8_t)(r & 0xff);
for (j = 0; j < 8; j++)
data[j] ^= iv[j];
iv -= 8;
data -= 8;
}
l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
Blowfish_decipher(c, &l, &r);
data[0] = (uint8_t)(l >> 24 & 0xff);
data[1] = (uint8_t)(l >> 16 & 0xff);
data[2] = (uint8_t)(l >> 8 & 0xff);
data[3] = (uint8_t)(l & 0xff);
data[4] = (uint8_t)(r >> 24 & 0xff);
data[5] = (uint8_t)(r >> 16 & 0xff);
data[6] = (uint8_t)(r >> 8 & 0xff);
data[7] = (uint8_t)(r & 0xff);
for (j = 0; j < 8; j++)
data[j] ^= iva[j];
}

View File

@@ -0,0 +1,150 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
//
// mpw-bench.c
// MasterPassword
//
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2014 Lyndir. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include "bcrypt.h"
#include "mpw-algorithm.h"
#include "mpw-util.h"
#define MP_N 32768
#define MP_r 8
#define MP_p 2
static void mpw_getTime(struct timeval *time) {
if (gettimeofday( time, NULL ) != 0)
ftl( "Could not get time: %s", strerror( 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 %lus %uµs -> %.2f/s\n", iterations, operation, dsec, dusec, speed );
return speed;
}
int main(int argc, char *const argv[]) {
const char *fullName = "Robert Lee Mitchel";
const char *masterPassword = "banana colored duckling";
const char *siteName = "masterpassword.app";
const MPCounterValue siteCounter = MPCounterValueDefault;
const MPResultType resultType = MPResultTypeDefault;
const MPKeyPurpose keyPurpose = MPKeyPurposeAuthentication;
const char *keyContext = NULL;
struct timeval startTime;
unsigned int iterations;
float percent;
MPMasterKey masterKey;
// Start HMAC-SHA-256
// Similar to phase-two of mpw
uint8_t *sitePasswordInfo = malloc( 128 );
iterations = 4200000; /* tuned to ~10s on dev machine */
masterKey = mpw_masterKey( fullName, masterPassword, MPAlgorithmVersionCurrent );
if (!masterKey) {
ftl( "Could not allocate master key: %s", strerror( errno ) );
abort();
}
mpw_getTime( &startTime );
for (int i = 1; i <= iterations; ++i) {
free( (void *)mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, sitePasswordInfo, 128 ) );
if (modff( 100.f * i / iterations, &percent ) == 0)
fprintf( stderr, "\rhmac-sha-256: iteration %d / %d (%.0f%%)..", i, iterations, percent );
}
const double hmacSha256Speed = mpw_showSpeed( startTime, iterations, "hmac-sha-256" );
free( (void *)masterKey );
// Start BCrypt
// Similar to phase-one of mpw
uint8_t bcrypt_rounds = 9;
iterations = 170; /* tuned to ~10s on dev machine */
mpw_getTime( &startTime );
for (int i = 1; i <= iterations; ++i) {
bcrypt( masterPassword, bcrypt_gensalt( bcrypt_rounds ) );
if (modff( 100.f * i / iterations, &percent ) == 0)
fprintf( stderr, "\rbcrypt (rounds 10^%d): iteration %d / %d (%.0f%%)..", bcrypt_rounds, i, iterations, percent );
}
const double bcrypt9Speed = mpw_showSpeed( startTime, iterations, "bcrypt" );
// Start SCrypt
// Phase one of mpw
iterations = 50; /* tuned to ~10s on dev machine */
mpw_getTime( &startTime );
for (int i = 1; i <= iterations; ++i) {
free( (void *)mpw_masterKey( fullName, masterPassword, MPAlgorithmVersionCurrent ) );
if (modff( 100.f * i / iterations, &percent ) == 0)
fprintf( stderr, "\rscrypt_mpw: iteration %d / %d (%.0f%%)..", i, iterations, percent );
}
const double scryptSpeed = mpw_showSpeed( startTime, iterations, "scrypt_mpw" );
// Start MPW
// Both phases of mpw
iterations = 50; /* tuned to ~10s on dev machine */
mpw_getTime( &startTime );
for (int i = 1; i <= iterations; ++i) {
masterKey = mpw_masterKey( fullName, masterPassword, MPAlgorithmVersionCurrent );
if (!masterKey) {
ftl( "Could not allocate master key: %s", strerror( errno ) );
break;
}
free( (void *)mpw_siteResult(
masterKey, siteName, siteCounter, keyPurpose, keyContext, resultType, NULL, MPAlgorithmVersionCurrent ) );
free( (void *)masterKey );
if (modff( 100.f * i / iterations, &percent ) == 0)
fprintf( stderr, "\rmpw: iteration %d / %d (%.0f%%)..", i, iterations, percent );
}
const double mpwSpeed = mpw_showSpeed( startTime, iterations, "mpw" );
// Summarize.
fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" );
fprintf( stdout, " - mpw is %f times slower than hmac-sha-256.\n", hmacSha256Speed / mpwSpeed );
fprintf( stdout, " - mpw is %f times slower than bcrypt (rounds 10^%d).\n", bcrypt9Speed / mpwSpeed, bcrypt_rounds );
fprintf( stdout, " - scrypt is %f times slower than bcrypt (rounds 10^%d).\n", bcrypt9Speed / scryptSpeed, bcrypt_rounds );
return 0;
}

View File

@@ -0,0 +1,348 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#define _POSIX_C_SOURCE 200809L
#include "mpw-cli-util.h"
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pwd.h>
#include <string.h>
#include <errno.h>
#include <sysexits.h>
#define MPW_MAX_INPUT 60
#if MPW_COLOR
#include <curses.h>
#include <term.h>
#endif
#include "mpw-util.h"
const char *mpw_getenv(const char *variableName) {
char *envBuf = getenv( variableName );
return envBuf? mpw_strdup( envBuf ): NULL;
}
char *mpw_askpass(const char *prompt) {
const char *askpass = mpw_getenv( MP_ENV_askpass );
if (!askpass)
return NULL;
int pipes[2];
if (pipe( pipes ) == ERR) {
wrn( "Couldn't pipe: %s", strerror( errno ) );
return NULL;
}
pid_t pid = fork();
if (pid == ERR) {
wrn( "Couldn't fork for askpass:\n %s: %s", askpass, strerror( errno ) );
return NULL;
}
if (!pid) {
// askpass fork
close( pipes[0] );
if (dup2( pipes[1], STDOUT_FILENO ) == ERR)
ftl( "Couldn't connect pipe to process: %s", strerror( errno ) );
else if (execlp( askpass, askpass, prompt, NULL ) == ERR)
ftl( "Couldn't execute askpass:\n %s: %s", askpass, strerror( errno ) );
exit( EX_SOFTWARE );
}
close( pipes[1] );
char *answer = mpw_read_fd( pipes[0] );
close( pipes[0] );
int status;
if (waitpid( pid, &status, 0 ) == ERR) {
wrn( "Couldn't wait for askpass: %s", strerror( errno ) );
mpw_free_string( &answer );
return NULL;
}
if (WIFEXITED( status ) && WEXITSTATUS( status ) == EXIT_SUCCESS && answer && strlen( answer )) {
// Remove trailing newline.
if (answer[strlen( answer ) - 1] == '\n')
answer[strlen( answer ) - 1] = '\0';
return answer;
}
mpw_free_string( &answer );
return NULL;
}
static const char *_mpw_getline(const char *prompt, bool silent) {
// Get answer from askpass.
char *answer = mpw_askpass( prompt );
if (answer)
return answer;
#if MPW_COLOR
// Initialize a curses screen.
SCREEN *screen = newterm( NULL, stderr, stdin );
start_color();
init_pair( 1, COLOR_WHITE, COLOR_BLUE );
init_pair( 2, COLOR_BLACK, COLOR_WHITE );
int rows, cols;
getmaxyx( stdscr, rows, cols );
// Display a dialog box.
int width = max( prompt? (int)strlen( prompt ): 0, MPW_MAX_INPUT ) + 6;
char *version = "mpw v" stringify_def( MP_VERSION );
mvprintw( rows - 1, (cols - (int)strlen( version )) / 2, "%s", version );
attron( A_BOLD );
color_set( 2, NULL );
mvprintw( rows / 2 - 1, (cols - width) / 2, "%s%*s%s", "*", width - 2, "", "*" );
mvprintw( rows / 2 - 1, (cols - (int)strlen( prompt )) / 2, "%s", prompt );
color_set( 1, NULL );
mvprintw( rows / 2 + 0, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
mvprintw( rows / 2 + 1, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
mvprintw( rows / 2 + 2, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
// Read response.
color_set( 2, NULL );
attron( A_STANDOUT );
int result = ERR;
char str[MPW_MAX_INPUT + 1];
if (silent) {
mvprintw( rows / 2 + 1, (cols - 5) / 2, "[ * ]" );
refresh();
noecho();
result = mvgetnstr( rows / 2 + 1, (cols - 1) / 2, str, MPW_MAX_INPUT );
echo();
} else {
mvprintw( rows / 2 + 1, (cols - (MPW_MAX_INPUT + 2)) / 2, "%*s", MPW_MAX_INPUT + 2, "" );
refresh();
echo();
result = mvgetnstr( rows / 2 + 1, (cols - MPW_MAX_INPUT) / 2, str, MPW_MAX_INPUT );
}
attrset( 0 );
endwin();
delscreen( screen );
return result == ERR? NULL: mpw_strndup( str, MPW_MAX_INPUT );
#else
// Get password from terminal.
fprintf( stderr, "%s ", prompt );
size_t bufSize = 0;
ssize_t lineSize = getline( &answer, &bufSize, stdin );
if (lineSize <= 1) {
mpw_free_string( &answer );
return NULL;
}
// Remove trailing newline.
answer[lineSize - 1] = '\0';
return answer;
#endif
}
const char *mpw_getline(const char *prompt) {
return _mpw_getline( prompt, false );
}
const char *mpw_getpass(const char *prompt) {
return _mpw_getline( prompt, true );
}
const char *mpw_path(const char *prefix, const char *extension) {
// Resolve user's home directory.
char *homeDir = NULL;
if (!homeDir)
if ((homeDir = getenv( "HOME" )))
homeDir = mpw_strdup( homeDir );
if (!homeDir)
if ((homeDir = getenv( "USERPROFILE" )))
homeDir = mpw_strdup( homeDir );
if (!homeDir) {
const char *homeDrive = getenv( "HOMEDRIVE" ), *homePath = getenv( "HOMEPATH" );
if (homeDrive && homePath)
homeDir = mpw_strdup( mpw_str( "%s%s", homeDrive, homePath ) );
}
if (!homeDir) {
struct passwd *passwd = getpwuid( getuid() );
if (passwd)
homeDir = mpw_strdup( passwd->pw_dir );
}
if (!homeDir)
homeDir = getcwd( NULL, 0 );
// Compose filename.
char *path = mpw_strdup( mpw_str( "%s.%s", prefix, extension ) );
// This is a filename, remove all potential directory separators.
for (char *slash; (slash = strstr( path, "/" )); *slash = '_');
// Compose pathname.
if (homeDir) {
const char *homePath = mpw_str( "%s/.mpw.d/%s", homeDir, path );
free( homeDir );
free( path );
if (homePath)
path = mpw_strdup( homePath );
}
return path;
}
bool mpw_mkdirs(const char *filePath) {
if (!filePath)
return false;
// Save the cwd and for absolute paths, start at the root.
char *cwd = getcwd( NULL, 0 );
if (*filePath == '/')
if (chdir( "/" ) == ERR)
return false;
// The path to mkdir is the filePath without the last path component.
char *pathEnd = strrchr( filePath, '/' );
if (!pathEnd)
return true;
// Walk the path.
bool success = true;
char *path = mpw_strndup( filePath, (size_t)(pathEnd - filePath) );
for (char *dirName = strtok( path, "/" ); success && dirName; dirName = strtok( NULL, "/" )) {
if (!strlen( dirName ))
continue;
success &= (mkdir( dirName, 0700 ) != ERR || errno == EEXIST) && chdir( dirName ) != ERR;
}
free( path );
if (chdir( cwd ) == ERR)
wrn( "Could not restore cwd:\n %s: %s", cwd, strerror( errno ) );
free( cwd );
return success;
}
char *mpw_read_fd(int fd) {
char *buf = NULL;
size_t blockSize = 4096, bufSize = 0, bufOffset = 0;
ssize_t readSize = 0;
while ((mpw_realloc( &buf, &bufSize, blockSize )) &&
((readSize = read( fd, buf + bufOffset, blockSize )) > 0));
if (readSize == ERR)
mpw_free( &buf, bufSize );
return buf;
}
char *mpw_read_file(FILE *file) {
if (!file)
return NULL;
char *buf = NULL;
size_t blockSize = 4096, bufSize = 0, bufOffset = 0, readSize = 0;
while ((mpw_realloc( &buf, &bufSize, blockSize )) &&
(bufOffset += (readSize = fread( buf + bufOffset, 1, blockSize, file ))) &&
(readSize == blockSize));
return buf;
}
#if MPW_COLOR
static char *str_tputs;
static int str_tputs_cursor;
static const int str_tputs_max = 256;
static bool mpw_setupterm() {
if (!isatty( STDERR_FILENO ))
return false;
static bool termsetup;
if (!termsetup) {
int errret;
if (!(termsetup = (setupterm( NULL, STDERR_FILENO, &errret ) == OK))) {
wrn( "Terminal doesn't support color (setupterm errret %d).", errret );
return false;
}
}
return true;
}
static int mpw_tputc(int c) {
if (++str_tputs_cursor < str_tputs_max) {
str_tputs[str_tputs_cursor] = (char)c;
return OK;
}
return ERR;
}
static char *mpw_tputs(const char *str, int affcnt) {
if (str_tputs)
mpw_free( &str_tputs, str_tputs_max );
str_tputs = calloc( str_tputs_max, sizeof( char ) );
str_tputs_cursor = -1;
char *result = tputs( str, affcnt, mpw_tputc ) == ERR? NULL: mpw_strndup( str_tputs, str_tputs_max );
if (str_tputs)
mpw_free( &str_tputs, str_tputs_max );
return result;
}
#endif
const char *mpw_identicon_str(MPIdenticon identicon) {
char *colorString, *resetString;
#ifdef MPW_COLOR
if (mpw_setupterm()) {
colorString = mpw_tputs( tparm( tgetstr( "AF", NULL ), identicon.color ), 1 );
resetString = mpw_tputs( tgetstr( "me", NULL ), 1 );
}
else
#endif
{
colorString = calloc( 1, sizeof( char ) );
resetString = calloc( 1, sizeof( char ) );
}
const char *str = mpw_str( "%s%s%s%s%s%s",
colorString, identicon.leftArm, identicon.body, identicon.rightArm, identicon.accessory, resetString );
mpw_free_strings( &colorString, &resetString, NULL );
return mpw_strdup( str );
}

View File

@@ -0,0 +1,69 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "mpw-types.h"
#ifndef MP_VERSION
#define MP_VERSION ?
#endif
#define MP_ENV_fullName "MPW_FULLNAME"
#define MP_ENV_algorithm "MPW_ALGORITHM"
#define MP_ENV_format "MPW_FORMAT"
#define MP_ENV_askpass "MPW_ASKPASS"
/** Read the value of an environment variable.
* @return A newly allocated string or NULL if the variable doesn't exist. */
const char *mpw_getenv(const char *variableName);
/** Use the askpass program to prompt the user.
* @return A newly allocated string or NULL if askpass is not supported or an error occurred. */
char *mpw_askpass(const char *prompt);
/** Ask the user a question.
* @return A newly allocated string or NULL if an error occurred trying to read from the user. */
const char *mpw_getline(const char *prompt);
/** Ask the user for a password.
* @return A newly allocated string or NULL if an error occurred trying to read from the user. */
const char *mpw_getpass(const char *prompt);
/** Get the absolute path to the mpw configuration file with the given prefix name and file extension.
* Resolves the file <prefix.extension> as located in the <.mpw.d> directory inside the user's home directory
* or current directory if it couldn't be resolved.
* @return A newly allocated string. */
const char *mpw_path(const char *prefix, const char *extension);
/** mkdir all the directories up to the directory of the given file path.
* @return true if the file's path exists. */
bool mpw_mkdirs(const char *filePath);
/** Read until EOF from the given file descriptor.
* @return A newly allocated string or NULL the read buffer couldn't be allocated. */
char *mpw_read_fd(int fd);
/** Read the file contents of a given file.
* @return A newly allocated string or NULL the read buffer couldn't be allocated. */
char *mpw_read_file(FILE *file);
/** Encode a visual fingerprint for a user.
* @return A newly allocated string. */
const char *mpw_identicon_str(MPIdenticon identicon);

View File

@@ -0,0 +1,801 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sysexits.h>
#include "mpw-cli-util.h"
#include "mpw-algorithm.h"
#include "mpw-util.h"
#include "mpw-marshal.h"
/** Output the program's usage documentation. */
static void usage() {
inf( ""
" Master Password v%s - CLI\n"
"--------------------------------------------------------------------------------\n"
" https://masterpassword.app\n", stringify_def( MP_VERSION ) );
inf( ""
"\nUSAGE\n\n"
" mpw [-u|-U full-name] [-m fd] [-t pw-type] [-P value] [-c counter]\n"
" [-a version] [-p purpose] [-C context] [-f|F format] [-R 0|1]\n"
" [-v|-q]* [-h] [site-name]\n" );
inf( ""
" -u full-name Specify the full name of the user.\n"
" -u checks the master password against the config,\n"
" -U allows updating to a new master password.\n"
" Defaults to %s in env or prompts.\n", MP_ENV_fullName );
dbg( ""
" -M master-pw Specify the master password of the user.\n"
" Passing secrets as arguments is unsafe, for use in testing only." );
inf( ""
" -m fd Read the master password of the user from a file descriptor.\n"
" Tip: don't send extra characters like newlines such as by using\n"
" echo in a pipe. Consider printf instead.\n" );
inf( ""
" -t pw-type Specify the password's template.\n"
" Defaults to 'long' (-p a), 'name' (-p i) or 'phrase' (-p r).\n"
" x, maximum | 20 characters, contains symbols.\n"
" l, long | Copy-friendly, 14 characters, symbols.\n"
" m, medium | Copy-friendly, 8 characters, 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"
" K, key | encryption key (512 bit or -P bits).\n"
" P, personal | saved personal password (save with -P pw).\n" );
inf( ""
" -P value The parameter value.\n"
" -p i | The login name for the site.\n"
" -t K | The bit size of the key to generate (eg. 256).\n"
" -t P | The personal password to encrypt.\n" );
inf( ""
" -c counter The value of the counter.\n"
" Defaults to 1.\n" );
inf( ""
" -a version The algorithm version to use, %d - %d.\n"
" Defaults to env var %s or %d.\n",
MPAlgorithmVersionFirst, MPAlgorithmVersionLast, MP_ENV_algorithm, MPAlgorithmVersionCurrent );
inf( ""
" -p purpose The purpose of the generated token.\n"
" Defaults to 'auth'.\n"
" a, auth | An authentication token such as a password.\n"
" i, ident | An identification token such as a username.\n"
" r, rec | A recovery token such as a security answer.\n" );
inf( ""
" -C context A purpose-specific context.\n"
" Defaults to empty.\n"
" -p a | -\n"
" -p i | -\n"
" -p r | Most significant word in security question.\n" );
inf( ""
" -f|F format The mpsites format to use for reading/writing site parameters.\n"
" -F forces the use of the given format,\n"
" -f allows fallback/migration.\n"
" Defaults to env var %s or json, falls back to plain.\n"
" n, none | No file\n"
" f, flat | ~/.mpw.d/Full Name.%s\n"
" j, json | ~/.mpw.d/Full Name.%s\n",
MP_ENV_format, mpw_marshal_format_extension( MPMarshalFormatFlat ), mpw_marshal_format_extension( MPMarshalFormatJSON ) );
inf( ""
" -R redacted Whether to save the mpsites in redacted format or not.\n"
" Redaction omits or encrypts any secrets, making the file safe\n"
" for saving on or transmitting via untrusted media.\n"
" Defaults to 1, redacted.\n" );
inf( ""
" -v Increase output verbosity (can be repeated).\n"
" -q Decrease output verbosity (can be repeated).\n" );
inf( ""
" -h Show this help output instead of performing any operation.\n" );
inf( ""
" site-name Name of the site for which to generate a token.\n" );
inf( ""
"\nENVIRONMENT\n\n"
" %-12s The full name of the user (see -u).\n"
" %-12s The default algorithm version (see -a).\n"
" %-12s The default mpsites format (see -f).\n"
" %-12s The askpass program to use for prompting the user.\n",
MP_ENV_fullName, MP_ENV_algorithm, MP_ENV_format, MP_ENV_askpass );
exit( EX_OK );
}
// Internal state.
typedef struct {
const char *fullName;
const char *masterPasswordFD;
const char *masterPassword;
const char *siteName;
const char *resultType;
const char *resultParam;
const char *siteCounter;
const char *algorithmVersion;
const char *keyPurpose;
const char *keyContext;
const char *sitesFormat;
const char *sitesRedacted;
} Arguments;
typedef struct {
bool allowPasswordUpdate;
bool sitesFormatFixed;
const char *fullName;
const char *masterPassword;
const char *identicon;
const char *siteName;
MPMarshalFormat sitesFormat;
MPKeyPurpose keyPurpose;
const char *keyContext;
const char *sitesPath;
MPResultType resultType;
MPCounterValue siteCounter;
const char *purposeResult;
const char *resultState;
const char *resultParam;
MPMarshalledUser *user;
MPMarshalledSite *site;
MPMarshalledQuestion *question;
} Operation;
// Processing steps.
void cli_free(Arguments *args, Operation *operation);
void cli_args(Arguments *args, Operation *operation, const int argc, char *const argv[]);
void cli_fullName(Arguments *args, Operation *operation);
void cli_masterPassword(Arguments *args, Operation *operation);
void cli_siteName(Arguments *args, Operation *operation);
void cli_sitesFormat(Arguments *args, Operation *operation);
void cli_keyPurpose(Arguments *args, Operation *operation);
void cli_keyContext(Arguments *args, Operation *operation);
void cli_user(Arguments *args, Operation *operation);
void cli_site(Arguments *args, Operation *operation);
void cli_question(Arguments *args, Operation *operation);
void cli_operation(Arguments *args, Operation *operation);
void cli_resultType(Arguments *args, Operation *operation);
void cli_siteCounter(Arguments *args, Operation *operation);
void cli_resultParam(Arguments *args, Operation *operation);
void cli_algorithmVersion(Arguments *args, Operation *operation);
void cli_sitesRedacted(Arguments *args, Operation *operation);
void cli_mpw(Arguments *args, Operation *operation);
void cli_save(Arguments *args, Operation *operation);
/** ========================================================================
* MAIN */
int main(const int argc, char *const argv[]) {
// Application defaults.
Arguments args = {
.fullName = mpw_getenv( MP_ENV_fullName ),
.algorithmVersion = mpw_getenv( MP_ENV_algorithm ),
.sitesFormat = mpw_getenv( MP_ENV_format ),
};
Operation operation = {
.allowPasswordUpdate = false,
.sitesFormatFixed = false,
.sitesFormat = MPMarshalFormatDefault,
.keyPurpose = MPKeyPurposeAuthentication,
.resultType = MPResultTypeDefault,
.siteCounter = MPCounterValueDefault,
};
// Read the command-line options.
cli_args( &args, &operation, argc, argv );
// Determine the operation parameters not sourced from mpsites.
cli_fullName( &args, &operation );
cli_masterPassword( &args, &operation );
cli_siteName( &args, &operation );
cli_sitesFormat( &args, &operation );
cli_keyPurpose( &args, &operation );
cli_keyContext( &args, &operation );
// Load the operation parameters from mpsites.
cli_user( &args, &operation );
cli_site( &args, &operation );
cli_question( &args, &operation );
cli_operation( &args, &operation );
// Override the operation parameters from command-line arguments.
cli_resultType( &args, &operation );
cli_siteCounter( &args, &operation );
cli_resultParam( &args, &operation );
cli_algorithmVersion( &args, &operation );
cli_sitesRedacted( &args, &operation );
cli_free( &args, NULL );
// Operation summary.
dbg( "-----------------" );
if (operation.user) {
dbg( "fullName : %s", operation.user->fullName );
trc( "masterPassword : %s", operation.user->masterPassword );
dbg( "identicon : %s", operation.identicon );
dbg( "sitesFormat : %s%s", mpw_nameForFormat( operation.sitesFormat ), operation.sitesFormatFixed? " (fixed)": "" );
dbg( "sitesPath : %s", operation.sitesPath );
}
if (operation.site) {
dbg( "siteName : %s", operation.site->name );
dbg( "siteCounter : %u", operation.siteCounter );
dbg( "resultType : %s (%u)", mpw_nameForType( operation.resultType ), operation.resultType );
dbg( "resultParam : %s", operation.resultParam );
dbg( "keyPurpose : %s (%u)", mpw_nameForPurpose( operation.keyPurpose ), operation.keyPurpose );
dbg( "keyContext : %s", operation.keyContext );
dbg( "algorithmVersion : %u", operation.site->algorithm );
}
dbg( "-----------------" );
// Finally ready to perform the actual operation.
cli_mpw( &args, &operation );
// Save changes and clean up.
cli_save( &args, &operation );
cli_free( &args, &operation );
return EX_OK;
}
void cli_free(Arguments *args, Operation *operation) {
if (args) {
mpw_free_strings( &args->fullName, &args->masterPasswordFD, &args->masterPassword, &args->siteName, NULL );
mpw_free_strings( &args->resultType, &args->resultParam, &args->siteCounter, &args->algorithmVersion, NULL );
mpw_free_strings( &args->keyPurpose, &args->keyContext, &args->sitesFormat, &args->sitesRedacted, NULL );
}
if (operation) {
mpw_free_strings( &operation->fullName, &operation->masterPassword, &operation->siteName, NULL );
mpw_free_strings( &operation->keyContext, &operation->resultState, &operation->resultParam, NULL );
mpw_free_strings( &operation->identicon, &operation->sitesPath, NULL );
mpw_marshal_free( &operation->user );
operation->site = NULL;
operation->question = NULL;
}
}
void cli_args(Arguments *args, Operation *operation, const int argc, char *const argv[]) {
for (int opt; (opt = getopt( argc, argv, "u:U:m:M:t:P:c:a:p:C:f:F:R:vqh" )) != EOF;
optarg? mpw_zero( optarg, strlen( optarg ) ): (void)0)
switch (opt) {
case 'u':
args->fullName = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
operation->allowPasswordUpdate = false;
break;
case 'U':
args->fullName = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
operation->allowPasswordUpdate = true;
break;
case 'm':
args->masterPasswordFD = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'M':
// Passing your master password via the command-line is insecure. Testing purposes only.
args->masterPassword = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 't':
args->resultType = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'P':
args->resultParam = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'c':
args->siteCounter = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'a':
args->algorithmVersion = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'p':
args->keyPurpose = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'C':
args->keyContext = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'f':
args->sitesFormat = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
operation->sitesFormatFixed = false;
break;
case 'F':
args->sitesFormat = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
operation->sitesFormatFixed = true;
break;
case 'R':
args->sitesRedacted = optarg && strlen( optarg )? mpw_strdup( optarg ): NULL;
break;
case 'v':
++mpw_verbosity;
break;
case 'q':
--mpw_verbosity;
break;
case 'h':
usage();
break;
case '?':
switch (optopt) {
case 'u':
ftl( "Missing full name to option: -%c", optopt );
exit( EX_USAGE );
case 't':
ftl( "Missing type name to option: -%c", optopt );
exit( EX_USAGE );
case 'c':
ftl( "Missing counter value to option: -%c", optopt );
exit( EX_USAGE );
default:
ftl( "Unknown option: -%c", optopt );
exit( EX_USAGE );
}
default:
ftl( "Unexpected option: %c", opt );
exit( EX_USAGE );
}
if (optind < argc && argv[optind])
args->siteName = mpw_strdup( argv[optind] );
}
void cli_fullName(Arguments *args, Operation *operation) {
mpw_free_string( &operation->fullName );
if (args->fullName)
operation->fullName = mpw_strdup( args->fullName );
if (!operation->fullName || !strlen( operation->fullName ))
do {
operation->fullName = mpw_getline( "Your full name:" );
} while (operation->fullName && !strlen( operation->fullName ));
if (!operation->fullName || !strlen( operation->fullName )) {
ftl( "Missing full name." );
cli_free( args, operation );
exit( EX_DATAERR );
}
}
void cli_masterPassword(Arguments *args, Operation *operation) {
mpw_free_string( &operation->masterPassword );
if (args->masterPasswordFD) {
operation->masterPassword = mpw_read_fd( atoi( args->masterPasswordFD ) );
if (!operation->masterPassword && errno)
wrn( "Error reading master password from FD %s: %s", args->masterPasswordFD, strerror( errno ) );
}
if (args->masterPassword && !operation->masterPassword)
operation->masterPassword = mpw_strdup( args->masterPassword );
if (!operation->masterPassword || !strlen( operation->masterPassword ))
do {
operation->masterPassword = mpw_getpass( "Your master password: " );
} while (operation->masterPassword && !strlen( operation->masterPassword ));
if (!operation->masterPassword || !strlen( operation->masterPassword )) {
ftl( "Missing master password." );
cli_free( args, operation );
exit( EX_DATAERR );
}
}
void cli_siteName(Arguments *args, Operation *operation) {
mpw_free_string( &operation->siteName );
if (args->siteName)
operation->siteName = mpw_strdup( args->siteName );
if (!operation->siteName)
operation->siteName = mpw_getline( "Site name:" );
if (!operation->siteName) {
ftl( "Missing site name." );
cli_free( args, operation );
exit( EX_DATAERR );
}
}
void cli_sitesFormat(Arguments *args, Operation *operation) {
if (!args->sitesFormat)
return;
operation->sitesFormat = mpw_formatWithName( args->sitesFormat );
if (ERR == (int)operation->sitesFormat) {
ftl( "Invalid sites format: %s", args->sitesFormat );
cli_free( args, operation );
exit( EX_DATAERR );
}
}
void cli_keyPurpose(Arguments *args, Operation *operation) {
if (!args->keyPurpose)
return;
operation->keyPurpose = mpw_purposeWithName( args->keyPurpose );
if (ERR == (int)operation->keyPurpose) {
ftl( "Invalid purpose: %s", args->keyPurpose );
cli_free( args, operation );
exit( EX_DATAERR );
}
}
void cli_keyContext(Arguments *args, Operation *operation) {
if (!args->keyContext)
return;
operation->keyContext = mpw_strdup( args->keyContext );
}
void cli_user(Arguments *args, Operation *operation) {
// Find mpsites file from parameters.
FILE *sitesFile = NULL;
mpw_free_string( &operation->sitesPath );
operation->sitesPath = mpw_path( operation->fullName, mpw_marshal_format_extension( operation->sitesFormat ) );
if (!operation->sitesPath || !(sitesFile = fopen( operation->sitesPath, "r" ))) {
dbg( "Couldn't open configuration file:\n %s: %s", operation->sitesPath, strerror( errno ) );
// Try to fall back to the flat format.
if (!operation->sitesFormatFixed) {
mpw_free_string( &operation->sitesPath );
operation->sitesPath = mpw_path( operation->fullName, mpw_marshal_format_extension( MPMarshalFormatFlat ) );
if (operation->sitesPath && (sitesFile = fopen( operation->sitesPath, "r" )))
operation->sitesFormat = MPMarshalFormatFlat;
else
dbg( "Couldn't open configuration file:\n %s: %s", operation->sitesPath, strerror( errno ) );
}
}
// Load the user object from mpsites.
if (!sitesFile)
mpw_free_string( &operation->sitesPath );
else {
// Read file.
char *sitesInputData = mpw_read_file( sitesFile );
if (ferror( sitesFile ))
wrn( "Error while reading configuration file:\n %s: %d", operation->sitesPath, ferror( sitesFile ) );
fclose( sitesFile );
// Parse file.
MPMarshalInfo *sitesInputInfo = mpw_marshal_read_info( sitesInputData );
MPMarshalFormat sitesInputFormat = args->sitesFormat? operation->sitesFormat: sitesInputInfo->format;
MPMarshalError marshalError = { .type = MPMarshalSuccess };
mpw_marshal_info_free( &sitesInputInfo );
mpw_marshal_free( &operation->user );
operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat, operation->masterPassword, &marshalError );
if (marshalError.type == MPMarshalErrorMasterPassword && operation->allowPasswordUpdate) {
// Update master password in mpsites.
while (marshalError.type == MPMarshalErrorMasterPassword) {
inf( "Given master password does not match configuration." );
inf( "To update the configuration with this new master password, first confirm the old master password." );
const char *importMasterPassword = NULL;
while (!importMasterPassword || !strlen( importMasterPassword ))
importMasterPassword = mpw_getpass( "Old master password: " );
mpw_marshal_free( &operation->user );
operation->user = mpw_marshal_read( sitesInputData, sitesInputFormat, importMasterPassword, &marshalError );
mpw_free_string( &importMasterPassword );
}
if (operation->user) {
mpw_free_string( &operation->user->masterPassword );
operation->user->masterPassword = mpw_strdup( operation->masterPassword );
}
}
mpw_free_string( &sitesInputData );
// Incorrect master password.
if (marshalError.type == MPMarshalErrorMasterPassword) {
ftl( "Incorrect master password according to configuration:\n %s: %s", operation->sitesPath, marshalError.description );
cli_free( args, operation );
exit( EX_DATAERR );
}
// Any other parse error.
if (!operation->user || marshalError.type != MPMarshalSuccess) {
err( "Couldn't parse configuration file:\n %s: %s", operation->sitesPath, marshalError.description );
cli_free( args, operation );
exit( EX_DATAERR );
}
}
// If no user from mpsites, create a new one.
if (!operation->user)
operation->user = mpw_marshal_user(
operation->fullName, operation->masterPassword, MPAlgorithmVersionCurrent );
}
void cli_site(Arguments __unused *args, Operation *operation) {
if (!operation->siteName)
abort();
// Load the site object from mpsites.
for (size_t s = 0; !operation->site && s < operation->user->sites_count; ++s)
if (strcmp( operation->siteName, (&operation->user->sites[s])->name ) == 0)
operation->site = &operation->user->sites[s];
// If no site from mpsites, create a new one.
if (!operation->site)
operation->site = mpw_marshal_site(
operation->user, operation->siteName, operation->user->defaultType, MPCounterValueDefault, operation->user->algorithm );
}
void cli_question(Arguments __unused *args, Operation *operation) {
if (!operation->site)
abort();
// Load the question object from mpsites.
switch (operation->keyPurpose) {
case MPKeyPurposeAuthentication:
case MPKeyPurposeIdentification:
break;
case MPKeyPurposeRecovery:
for (size_t q = 0; !operation->question && q < operation->site->questions_count; ++q)
if ((!operation->keyContext && !strlen( (&operation->site->questions[q])->keyword )) ||
(operation->keyContext && strcmp( (&operation->site->questions[q])->keyword, operation->keyContext ) == 0))
operation->question = &operation->site->questions[q];
// If no question from mpsites, create a new one.
if (!operation->question)
operation->question = mpw_marshal_question( operation->site, operation->keyContext );
break;
}
}
void cli_operation(Arguments __unused *args, Operation *operation) {
mpw_free_string( &operation->identicon );
operation->identicon = mpw_identicon_str( mpw_identicon( operation->user->fullName, operation->user->masterPassword ) );
if (!operation->site)
abort();
switch (operation->keyPurpose) {
case MPKeyPurposeAuthentication: {
operation->purposeResult = "password";
operation->resultType = operation->site->type;
operation->resultState = operation->site->content? mpw_strdup( operation->site->content ): NULL;
operation->siteCounter = operation->site->counter;
break;
}
case MPKeyPurposeIdentification: {
operation->purposeResult = "login";
operation->resultType = operation->site->loginType;
operation->resultState = operation->site->loginContent? mpw_strdup( operation->site->loginContent ): NULL;
operation->siteCounter = MPCounterValueInitial;
break;
}
case MPKeyPurposeRecovery: {
mpw_free_string( &operation->keyContext );
operation->purposeResult = "answer";
operation->keyContext = operation->question->keyword? mpw_strdup( operation->question->keyword ): NULL;
operation->resultType = operation->question->type;
operation->resultState = operation->question->content? mpw_strdup( operation->question->content ): NULL;
operation->siteCounter = MPCounterValueInitial;
break;
}
}
}
void cli_resultType(Arguments *args, Operation *operation) {
if (!args->resultType)
return;
if (!operation->site)
abort();
operation->resultType = mpw_typeWithName( args->resultType );
if (ERR == (int)operation->resultType) {
ftl( "Invalid type: %s", args->resultType );
cli_free( args, operation );
exit( EX_USAGE );
}
if (!(operation->resultType & MPSiteFeatureAlternative)) {
switch (operation->keyPurpose) {
case MPKeyPurposeAuthentication:
operation->site->type = operation->resultType;
break;
case MPKeyPurposeIdentification:
operation->site->loginType = operation->resultType;
break;
case MPKeyPurposeRecovery:
operation->question->type = operation->resultType;
break;
}
}
}
void cli_siteCounter(Arguments *args, Operation *operation) {
if (!args->siteCounter)
return;
if (!operation->site)
abort();
long long int siteCounterInt = atoll( args->siteCounter );
if (siteCounterInt < MPCounterValueFirst || siteCounterInt > MPCounterValueLast) {
ftl( "Invalid site counter: %s", args->siteCounter );
cli_free( args, operation );
exit( EX_USAGE );
}
switch (operation->keyPurpose) {
case MPKeyPurposeAuthentication:
operation->siteCounter = operation->site->counter = (MPCounterValue)siteCounterInt;
break;
case MPKeyPurposeIdentification:
case MPKeyPurposeRecovery:
// NOTE: counter for login & question is not persisted.
break;
}
}
void cli_resultParam(Arguments *args, Operation *operation) {
if (!args->resultParam)
return;
mpw_free_string( &operation->resultParam );
operation->resultParam = mpw_strdup( args->resultParam );
}
void cli_algorithmVersion(Arguments *args, Operation *operation) {
if (!args->algorithmVersion)
return;
if (!operation->site)
abort();
int algorithmVersionInt = atoi( args->algorithmVersion );
if (algorithmVersionInt < MPAlgorithmVersionFirst || algorithmVersionInt > MPAlgorithmVersionLast) {
ftl( "Invalid algorithm version: %s", args->algorithmVersion );
cli_free( args, operation );
exit( EX_USAGE );
}
operation->site->algorithm = (MPAlgorithmVersion)algorithmVersionInt;
}
void cli_sitesRedacted(Arguments *args, Operation *operation) {
if (args->sitesRedacted)
operation->user->redacted = strcmp( args->sitesRedacted, "1" ) == 0;
else if (!operation->user->redacted)
wrn( "Sites configuration is not redacted. Use -R 1 to change this." );
}
void cli_mpw(Arguments *args, Operation *operation) {
if (!operation->site)
abort();
if (mpw_verbosity >= inf_level)
fprintf( stderr, "%s's %s for %s:\n[ %s ]: ",
operation->user->fullName, operation->purposeResult, operation->site->name, operation->identicon );
// Determine master key.
MPMasterKey masterKey = mpw_masterKey(
operation->user->fullName, operation->user->masterPassword, operation->site->algorithm );
if (!masterKey) {
ftl( "Couldn't derive master key." );
cli_free( args, operation );
exit( EX_SOFTWARE );
}
// Update state from resultParam if stateful.
if (operation->resultParam && operation->resultType & MPResultTypeClassStateful) {
mpw_free_string( &operation->resultState );
if (!(operation->resultState = mpw_siteState( masterKey, operation->site->name, operation->siteCounter,
operation->keyPurpose, operation->keyContext, operation->resultType, operation->resultParam,
operation->site->algorithm ))) {
ftl( "Couldn't encrypt site result." );
mpw_free( &masterKey, MPMasterKeySize );
cli_free( args, operation );
exit( EX_SOFTWARE );
}
inf( "(state) %s => ", operation->resultState );
switch (operation->keyPurpose) {
case MPKeyPurposeAuthentication: {
mpw_free_string( &operation->site->content );
operation->site->content = mpw_strdup( operation->resultState );
break;
}
case MPKeyPurposeIdentification: {
mpw_free_string( &operation->site->loginContent );
operation->site->loginContent = mpw_strdup( operation->resultState );
break;
}
case MPKeyPurposeRecovery: {
mpw_free_string( &operation->question->content );
operation->question->content = mpw_strdup( operation->resultState );
break;
}
}
// resultParam is consumed.
mpw_free_string( &operation->resultParam );
}
// resultParam defaults to state.
if (!operation->resultParam && operation->resultState)
operation->resultParam = mpw_strdup( operation->resultState );
// Generate result.
const char *result = mpw_siteResult( masterKey, operation->site->name, operation->siteCounter,
operation->keyPurpose, operation->keyContext, operation->resultType, operation->resultParam, operation->site->algorithm );
mpw_free( &masterKey, MPMasterKeySize );
if (!result) {
ftl( "Couldn't generate site result." );
cli_free( args, operation );
exit( EX_SOFTWARE );
}
fflush( NULL );
fprintf( stdout, "%s\n", result );
if (operation->site->url)
inf( "See: %s", operation->site->url );
mpw_free_string( &result );
// Update usage metadata.
operation->site->lastUsed = operation->user->lastUsed = time( NULL );
operation->site->uses++;
}
void cli_save(Arguments __unused *args, Operation *operation) {
if (operation->sitesFormat == MPMarshalFormatNone)
return;
if (!operation->sitesFormatFixed)
operation->sitesFormat = MPMarshalFormatDefault;
mpw_free_string( &operation->sitesPath );
operation->sitesPath = mpw_path( operation->user->fullName, mpw_marshal_format_extension( operation->sitesFormat ) );
dbg( "Updating: %s (%s)", operation->sitesPath, mpw_nameForFormat( operation->sitesFormat ) );
FILE *sitesFile = NULL;
if (!operation->sitesPath || !mpw_mkdirs( operation->sitesPath ) || !(sitesFile = fopen( operation->sitesPath, "w" ))) {
wrn( "Couldn't create updated configuration file:\n %s: %s", operation->sitesPath, strerror( errno ) );
return;
}
char *buf = NULL;
MPMarshalError marshalError = { .type = MPMarshalSuccess };
if (!mpw_marshal_write( &buf, operation->sitesFormat, operation->user, &marshalError ) || marshalError.type != MPMarshalSuccess)
wrn( "Couldn't encode updated configuration file:\n %s: %s", operation->sitesPath, marshalError.description );
else if (fwrite( buf, sizeof( char ), strlen( buf ), sitesFile ) != strlen( buf ))
wrn( "Error while writing updated configuration file:\n %s: %d", operation->sitesPath, ferror( sitesFile ) );
mpw_free_string( &buf );
fclose( sitesFile );
}

View File

@@ -0,0 +1,95 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
//
// 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 = id && xmlStrcmp( id, parentId ) == 0;
xmlFree( id );
if (foundParent) {
xmlFree( parentId );
return mpw_xmlTestCaseNode( otherTestCaseNode, nodeName );
}
}
err( "Missing parent: %s, for case: %s", parentId, mpw_xmlTestCaseString( testCaseNode, "id" ) );
return NULL;
}
xmlChar *mpw_xmlTestCaseString(xmlNodePtr context, const char *nodeName) {
xmlNodePtr child = mpw_xmlTestCaseNode( context, nodeName );
return child? xmlNodeGetContent( child ): NULL;
}
uint32_t mpw_xmlTestCaseInteger(xmlNodePtr context, const char *nodeName) {
xmlChar *string = mpw_xmlTestCaseString( context, nodeName );
uint32_t integer = string? (uint32_t)atol( (char *)string ): 0;
xmlFree( string );
return integer;
}

View File

@@ -0,0 +1,34 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
//
// 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,177 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
#ifndef mpw_log_do
#define mpw_log_do(level, format, ...) ({ \
fprintf( stderr, format "\n", ##__VA_ARGS__ ); \
if (level == ftl_level) \
abort(); \
})
#endif
#include "mpw-algorithm.h"
#include "mpw-util.h"
#include "mpw-tests-util.h"
/** Output the program's usage documentation. */
static void usage() {
inf( ""
" Master Password v%s - Tests\n"
"--------------------------------------------------------------------------------\n"
" https://masterpassword.app\n", stringify_def( MP_VERSION ) );
inf( ""
"\nUSAGE\n\n"
" mpw-tests [-v|-q]* [-h] [test-name ...]\n" );
inf( ""
" -v Increase output verbosity (can be repeated).\n"
" -q Decrease output verbosity (can be repeated).\n" );
inf( ""
" -h Show this help output instead of performing any operation.\n" );
inf( ""
" test-name Only run tests whose identifier starts with one of the these.\n" );
exit( EX_OK );
}
int main(int argc, char *const argv[]) {
for (int opt; (opt = getopt( argc, argv, "vqh" )) != EOF;
optarg? mpw_zero( optarg, strlen( optarg ) ): (void)0)
switch (opt) {
case 'v':
++mpw_verbosity;
break;
case 'q':
--mpw_verbosity;
break;
case 'h':
usage();
break;
case '?':
ftl( "Unknown option: -%c", optopt );
exit( EX_USAGE );
default:
ftl( "Unexpected option: %c", opt );
exit( EX_USAGE );
}
int failedTests = 0;
xmlNodePtr tests = xmlDocGetRootElement( xmlParseFile( "mpw_tests.xml" ) );
if (!tests) {
ftl( "Couldn't find test case: mpw_tests.xml" );
abort();
}
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" );
MPAlgorithmVersion algorithm = (MPAlgorithmVersion)mpw_xmlTestCaseInteger( testCase, "algorithm" );
xmlChar *fullName = mpw_xmlTestCaseString( testCase, "fullName" );
xmlChar *masterPassword = mpw_xmlTestCaseString( testCase, "masterPassword" );
xmlChar *keyID = mpw_xmlTestCaseString( testCase, "keyID" );
xmlChar *siteName = mpw_xmlTestCaseString( testCase, "siteName" );
MPCounterValue siteCounter = (MPCounterValue)mpw_xmlTestCaseInteger( testCase, "siteCounter" );
xmlChar *resultTypeString = mpw_xmlTestCaseString( testCase, "resultType" );
xmlChar *keyPurposeString = mpw_xmlTestCaseString( testCase, "keyPurpose" );
xmlChar *keyContext = mpw_xmlTestCaseString( testCase, "keyContext" );
xmlChar *result = mpw_xmlTestCaseString( testCase, "result" );
MPResultType resultType = mpw_typeWithName( (char *)resultTypeString );
MPKeyPurpose keyPurpose = mpw_purposeWithName( (char *)keyPurposeString );
// Run the test case.
do {
if (optind < argc) {
bool selected = false;
for (int a = optind; !selected && a <= argc; ++a)
if (strstr((char *)id, argv[optind]) == (char *)id)
selected = true;
if (!selected)
continue;
}
fprintf( stdout, "test case %s... ", id );
if (!xmlStrlen( result )) {
fprintf( stdout, "abstract." );
continue;
}
// 1. calculate the master key.
MPMasterKey masterKey = mpw_masterKey(
(char *)fullName, (char *)masterPassword, algorithm );
if (!masterKey) {
ftl( "Couldn't derive master key." );
abort();
}
// Check the master key.
MPKeyID testKeyID = mpw_id_buf( masterKey, MPMasterKeySize );
if (xmlStrcmp( keyID, BAD_CAST testKeyID ) != 0) {
++failedTests;
fprintf( stdout, "FAILED! (keyID: got %s != expected %s)\n", testKeyID, keyID );
continue;
}
// 2. calculate the site password.
const char *testResult = mpw_siteResult(
masterKey, (char *)siteName, siteCounter, keyPurpose, (char *)keyContext, resultType, NULL, algorithm );
mpw_free( &masterKey, MPMasterKeySize );
if (!testResult) {
ftl( "Couldn't derive site password." );
continue;
}
// Check the site result.
if (xmlStrcmp( result, BAD_CAST testResult ) != 0) {
++failedTests;
fprintf( stdout, "FAILED! (result: got %s != expected %s)\n", testResult, result );
mpw_free_string( &testResult );
continue;
}
mpw_free_string( &testResult );
fprintf( stdout, "pass.\n" );
} while(false);
// Free test case.
xmlFree( id );
xmlFree( fullName );
xmlFree( masterPassword );
xmlFree( keyID );
xmlFree( siteName );
xmlFree( resultTypeString );
xmlFree( keyPurposeString );
xmlFree( keyContext );
xmlFree( result );
}
return failedTests;
}

View File

@@ -0,0 +1,80 @@
import org.gradle.internal.jvm.Jvm
plugins {
id 'cpp-library'
id 'base'
}
description = 'Master Password Algorithm Implementation'
artifacts {
'default' task( type: Zip, "archive" ) {
components.withType( ComponentWithRuntimeFile ) {
if (isOptimized()) {
from getRuntimeFile()
into standardOperatingSystem( linkTask.get().targetPlatform.get() ) + '/' +
standardArchitecture( linkTask.get().targetPlatform.get() )
}
}
}
}
library {
linkage.set( [Linkage.STATIC, Linkage.SHARED] )
// Reconfigure the toolchain from C++ to C.
toolChains {
withType( GccCompatibleToolChain ) {
//TODO: Cross-compiling, blocked by: https://github.com/gradle/gradle-native/issues/169
//setTargets( "arm", "arm64", "x86-64", "x86" )
eachPlatform {
cppCompiler.withArguments { addAll( ["-x", "c", "-std=c11", "-Werror", "-DMPW_SODIUM=1"] ) }
}
}
}
components.withType( CppComponent ) {
cppSource.from fileTree( dir: "src", include: "**/*.c" )
}
// Cross-compile for these host platforms.
operatingSystems.set( [objects.named( OperatingSystemFamily, OperatingSystemFamily.WINDOWS ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.LINUX ),
objects.named( OperatingSystemFamily, OperatingSystemFamily.MAC_OS )] )
//
binaries.configureEach {
// Resolve a standard name for the platform.
def platform = standardOperatingSystem( targetPlatform )
project.dependencies {
// Depend on JDK for JNI support.
add( includePathConfiguration.name,
files( new File( Jvm.current().javaHome, "include" ) ) { first().eachDir { from it } } )
// Depend on libsodium from `lib`; run `lib/bin/build_libsodium-${os}` first.
add( includePathConfiguration.name,
files( "../../lib/libsodium/build-${platform}~/out/include" ) )
add( linkLibraries.name,
fileTree( "../../lib/libsodium/build-${platform}~/out/lib" ) )
}
}
}
static String standardOperatingSystem(NativePlatform platform) {
OperatingSystem os = platform.getOperatingSystem()
if (os.isWindows()) {
return OperatingSystemFamily.WINDOWS
} else if (os.isLinux()) {
return OperatingSystemFamily.LINUX
} else if (os.isMacOsX()) {
return OperatingSystemFamily.MAC_OS
}
return os.name.toLowerCase()
}
static String standardArchitecture(NativePlatform platform) {
Architecture arch = platform.getArchitecture()
return arch.name.toLowerCase().replaceAll( "-", "_" )
}

View File

@@ -0,0 +1,606 @@
/*
Source: https://github.com/kokke/tiny-AES-c
This is an implementation of the AES algorithm, specifically ECB and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the block size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <string.h>
#include "aes.h"
#include "mpw-util.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
#if defined(AES_256) && (AES_256 == 1)
#define Nk 8
#define KEYLEN 32
#define Nr 14
#define keyExpSize 240
#elif defined(AES_192) && (AES_192 == 1)
#define Nk 6
#define KEYLEN 24
#define Nr 12
#define keyExpSize 208
#elif defined(AES_128) && (AES_128 == 1)
#define Nk 4 // The number of 32 bit words in a key.
#define KEYLEN 16 // Key length in bytes
#define Nr 10 // The number of rounds in AES Cipher.
#define keyExpSize 176
#else
#error Must define either AES_128, AES_192 or AES_256.
#endif
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES128-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
static state_t* state;
// The array that stores the round keys.
static uint8_t RoundKey[keyExpSize];
// The Key input to the AES Program
static const uint8_t* Key;
#if defined(AES_CBC) && AES_CBC
// Initial Vector used only for CBC mode
static uint8_t* Iv;
#endif
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t rsbox[256] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
// The round constant word array, Rcon[i], contains the values given by
// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES128-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*
* ... which is why the full array below has been 'disabled' below.
*/
#if 0
static const uint8_t Rcon[256] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d };
#endif
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(void)
{
uint32_t i;
uint8_t k, tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
//i == Nk
for (; i < Nb * (Nr + 1); ++i)
{
{
tempa[0]=RoundKey[(i-1) * 4 + 0];
tempa[1]=RoundKey[(i-1) * 4 + 1];
tempa[2]=RoundKey[(i-1) * 4 + 2];
tempa[3]=RoundKey[(i-1) * 4 + 3];
}
if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
k = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = k;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
RoundKey[i * 4 + 0] = RoundKey[(i - Nk) * 4 + 0] ^ tempa[0];
RoundKey[i * 4 + 1] = RoundKey[(i - Nk) * 4 + 1] ^ tempa[1];
RoundKey[i * 4 + 2] = RoundKey[(i - Nk) * 4 + 2] ^ tempa[2];
RoundKey[i * 4 + 3] = RoundKey[(i - Nk) * 4 + 3] ^ tempa[3];
}
}
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round)
{
uint8_t i,j;
for (i=0;i<4;++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(void)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(void)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return (uint8_t)((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(void)
{
uint8_t i;
uint8_t Tmp,Tm,t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))));
}
#else
#define Multiply(x, y) (uint8_t) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(void)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(void)
{
uint8_t i,j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(void)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
// Cipher is the main function that encrypts the PlainText.
static void Cipher(void)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = 1; round < Nr; ++round)
{
SubBytes();
ShiftRows();
MixColumns();
AddRoundKey(round);
}
// The last round is given below.
// The MixColumns function is not here in the last round.
SubBytes();
ShiftRows();
AddRoundKey(Nr);
}
static void InvCipher(void)
{
uint8_t round=0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (round = (Nr - 1); round > 0; --round)
{
InvShiftRows();
InvSubBytes();
AddRoundKey(round);
InvMixColumns();
}
// The last round is given below.
// The MixColumns function is not here in the last round.
InvShiftRows();
InvSubBytes();
AddRoundKey(0);
}
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(AES_ECB) && (AES_ECB == 1)
void AES_ECB_encrypt(uint8_t *output, const uint8_t *input, const uint32_t length, const uint8_t *key)
{
// Copy input to output, and work in-memory on output
memcpy(output, input, length);
state = (state_t*)output;
Key = key;
KeyExpansion();
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher();
mpw_zero( RoundKey, keyExpSize );
}
void AES_ECB_decrypt(uint8_t *output, const uint8_t *input, const uint32_t length, const uint8_t *key)
{
// Copy input to output, and work in-memory on output
memcpy(output, input, length);
state = (state_t*)output;
// The KeyExpansion routine must be called before encryption.
Key = key;
KeyExpansion();
InvCipher();
mpw_zero( RoundKey, keyExpSize );
}
#endif // #if defined(AES_ECB) && (AES_ECB == 1)
#if defined(AES_CBC) && (AES_CBC == 1)
static void XorWithIv(uint8_t* buf)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) //WAS for(i = 0; i < KEYLEN; ++i) but the block in AES is always 128bit so 16 bytes!
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
{
uintptr_t i;
uint8_t extra = (uint8_t)(length % AES_BLOCKLEN); /* Remaining bytes in the last non-full block */
// Skip the key expansion if key is passed as 0
if (0 != key)
{
Key = key;
KeyExpansion();
}
if (iv != 0)
{
Iv = (uint8_t*)iv;
}
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(input);
memcpy(output, input, AES_BLOCKLEN);
state = (state_t*)output;
Cipher();
Iv = output;
input += AES_BLOCKLEN;
output += AES_BLOCKLEN;
//printf("Step %d - %d", i/16, i);
}
if (extra)
{
memcpy(output, input, extra);
state = (state_t*)output;
Cipher();
}
mpw_zero( RoundKey, keyExpSize );
}
void AES_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, const uint8_t* key, const uint8_t* iv)
{
uintptr_t i;
uint8_t extra = (uint8_t)(length % AES_BLOCKLEN); /* Remaining bytes in the last non-full block */
// Skip the key expansion if key is passed as 0
if (0 != key)
{
Key = key;
KeyExpansion();
}
// If iv is passed as 0, we continue to encrypt without re-setting the Iv
if (iv != 0)
{
Iv = (uint8_t*)iv;
}
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(output, input, AES_BLOCKLEN);
state = (state_t*)output;
InvCipher();
XorWithIv(output);
Iv = input;
input += AES_BLOCKLEN;
output += AES_BLOCKLEN;
}
if (extra)
{
memcpy(output, input, extra);
state = (state_t*)output;
InvCipher();
}
mpw_zero( RoundKey, keyExpSize );
}
#endif // #if defined(AES_CBC) && (AES_CBC == 1)

View File

@@ -0,0 +1,50 @@
/*
Source: https://github.com/kokke/tiny-AES-c
This is an implementation of the AES algorithm, specifically ECB and CBC mode.
*/
#ifndef _AES_H_
#define _AES_H_
#include <stdint.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// AES_CBC enables AES encryption in CBC-mode of operation.
// AES_ECB enables the basic ECB 16-byte block algorithm. Both can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef AES_CBC
#define AES_CBC 1
#endif
#ifndef AES_ECB
#define AES_ECB 1
#endif
#define AES_128 1
//#define AES_192 1
//#define AES_256 1
#define AES_BLOCKLEN 16 //Block length in bytes AES is 128b block only
#if defined(AES_ECB) && (AES_ECB == 1)
void AES_ECB_encrypt(uint8_t *output, const uint8_t *input, const uint32_t length, const uint8_t *key);
void AES_ECB_decrypt(uint8_t *output, const uint8_t *input, const uint32_t length, const uint8_t *key);
#endif // #if defined(AES_ECB) && (AES_ECB == !)
#if defined(AES_CBC) && (AES_CBC == 1)
void AES_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, const uint32_t length, const uint8_t* key, const uint8_t* iv);
void AES_CBC_decrypt_buffer(uint8_t* output, uint8_t* input, const uint32_t length, const uint8_t* key, const uint8_t* iv);
#endif // #if defined(AES_CBC) && (AES_CBC == 1)
#endif //_AES_H_

View File

@@ -0,0 +1,155 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*/
#include "base64.h"
/* aaaack but it's fast and const should make it shared text page. */
static const uint8_t b64ToBits[256] =
{
/* ASCII table */
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};
size_t mpw_base64_decode_max(const char *b64Text) {
register const uint8_t *b64Cursor = (uint8_t *)b64Text;
while (b64ToBits[*(b64Cursor++)] <= 63);
int b64Size = (int)(b64Cursor - (uint8_t *)b64Text) - 1;
// Every 4 b64 chars yield 3 plain bytes => len = 3 * ceil(b64Size / 4)
return (size_t)(3 /*bytes*/ * ((b64Size + 4 /*chars*/ - 1) / 4 /*chars*/));
}
int mpw_base64_decode(uint8_t *plainBuf, const char *b64Text) {
register const uint8_t *b64Cursor = (uint8_t *)b64Text;
while (b64ToBits[*(b64Cursor++)] <= 63);
int b64Remaining = (int)(b64Cursor - (uint8_t *)b64Text) - 1;
b64Cursor = (uint8_t *)b64Text;
register uint8_t *plainCursor = plainBuf;
while (b64Remaining > 4) {
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[0]] << 2 | b64ToBits[b64Cursor[1]] >> 4);
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[1]] << 4 | b64ToBits[b64Cursor[2]] >> 2);
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[2]] << 6 | b64ToBits[b64Cursor[3]]);
b64Cursor += 4;
b64Remaining -= 4;
}
/* Note: (b64Size == 1) would be an error, so just ingore that case */
if (b64Remaining > 1)
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[0]] << 2 | b64ToBits[b64Cursor[1]] >> 4);
if (b64Remaining > 2)
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[1]] << 4 | b64ToBits[b64Cursor[2]] >> 2);
if (b64Remaining > 3)
*(plainCursor++) = (uint8_t)(b64ToBits[b64Cursor[2]] << 6 | b64ToBits[b64Cursor[3]]);
return (int)(plainCursor - plainBuf);
}
static const char basis_64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t mpw_base64_encode_max(size_t plainSize) {
// Every 3 plain bytes yield 4 b64 chars => len = 4 * ceil(plainSize / 3)
return 4 /*chars*/ * (plainSize + 3 /*bytes*/ - 1) / 3 /*bytes*/;
}
int mpw_base64_encode(char *b64Text, const uint8_t *plainBuf, size_t plainSize) {
size_t plainCursor = 0;
char *b64Cursor = b64Text;
for (; plainCursor < plainSize - 2; plainCursor += 3) {
*b64Cursor++ = basis_64[((plainBuf[plainCursor] >> 2)) & 0x3F];
*b64Cursor++ = basis_64[((plainBuf[plainCursor] & 0x3) << 4) |
((plainBuf[plainCursor + 1] & 0xF0) >> 4)];
*b64Cursor++ = basis_64[((plainBuf[plainCursor + 1] & 0xF) << 2) |
((plainBuf[plainCursor + 2] & 0xC0) >> 6)];
*b64Cursor++ = basis_64[plainBuf[plainCursor + 2] & 0x3F];
}
if (plainCursor < plainSize) {
*b64Cursor++ = basis_64[(plainBuf[plainCursor] >> 2) & 0x3F];
if (plainCursor == (plainSize - 1)) {
*b64Cursor++ = basis_64[((plainBuf[plainCursor] & 0x3) << 4)];
*b64Cursor++ = '=';
}
else {
*b64Cursor++ = basis_64[((plainBuf[plainCursor] & 0x3) << 4) |
((plainBuf[plainCursor + 1] & 0xF0) >> 4)];
*b64Cursor++ = basis_64[((plainBuf[plainCursor + 1] & 0xF) << 2)];
}
*b64Cursor++ = '=';
}
*b64Cursor = '\0';
return (int)(b64Cursor - b64Text);
}

View File

@@ -0,0 +1,78 @@
/* ====================================================================
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* 4. The names "Apache Server" and "Apache Group" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by the Apache Group
* for use in the Apache HTTP server project (http://www.apache.org/)."
*
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Group and was originally based
* on public domain software written at the National Center for
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
* For more information on the Apache Group and the Apache HTTP server
* project, please see <http://www.apache.org/>.
*/
#include <stddef.h>
#include <stdint.h>
/**
* @return The amount of bytes needed to decode the given b64Text.
*/
size_t mpw_base64_decode_max(const char *b64Text);
/** Decodes a base-64 encoded string into a plain byte buffer.
* @param plainBuf a byte buffer, size should be at least mpw_base64_decode_max(b64Text)
* @return The amount of bytes that were written to plainBuf.
*/
int mpw_base64_decode(uint8_t *plainBuf, const char *b64Text);
/**
* @return The amount of characters needed to encode a plainBuf of the given size as base-64 (excluding the terminating NUL).
*/
size_t mpw_base64_encode_max(size_t plainSize);
/** Encodes a plain byte buffer into a base-64 encoded string.
* @param b64Text a character buffer, size should be at least mpw_base64_encode_max(plainSize) + 1
* @return The amount of characters that were written to b64Text, excluding the terminating NUL.
*/
int mpw_base64_encode(char *b64Text, const uint8_t *plainBuf, size_t plainSize);

View File

@@ -0,0 +1,226 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include "mpw-algorithm.h"
#include "mpw-algorithm_v0.c"
#include "mpw-algorithm_v1.c"
#include "mpw-algorithm_v2.c"
#include "mpw-algorithm_v3.c"
MPMasterKey mpw_masterKey(const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
if (fullName && !strlen( fullName ))
fullName = NULL;
if (masterPassword && !strlen( masterPassword ))
masterPassword = NULL;
trc( "-- mpw_masterKey (algorithm: %u)", algorithmVersion );
trc( "fullName: %s", fullName );
trc( "masterPassword.id: %s", masterPassword? mpw_id_buf( masterPassword, strlen( masterPassword ) ): NULL );
if (!fullName || !masterPassword)
return NULL;
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_masterKey_v0( fullName, masterPassword );
case MPAlgorithmVersion1:
return mpw_masterKey_v1( fullName, masterPassword );
case MPAlgorithmVersion2:
return mpw_masterKey_v2( fullName, masterPassword );
case MPAlgorithmVersion3:
return mpw_masterKey_v3( fullName, masterPassword );
default:
err( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}
MPSiteKey mpw_siteKey(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion) {
if (keyContext && !strlen( keyContext ))
keyContext = NULL;
trc( "-- mpw_siteKey (algorithm: %u)", algorithmVersion );
trc( "siteName: %s", siteName );
trc( "siteCounter: %d", siteCounter );
trc( "keyPurpose: %d (%s)", keyPurpose, mpw_nameForPurpose( keyPurpose ) );
trc( "keyContext: %s", keyContext );
if (!masterKey || !siteName)
return NULL;
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext );
case MPAlgorithmVersion1:
return mpw_siteKey_v1( masterKey, siteName, siteCounter, keyPurpose, keyContext );
case MPAlgorithmVersion2:
return mpw_siteKey_v2( masterKey, siteName, siteCounter, keyPurpose, keyContext );
case MPAlgorithmVersion3:
return mpw_siteKey_v3( masterKey, siteName, siteCounter, keyPurpose, keyContext );
default:
err( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}
const char *mpw_siteResult(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext,
const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion) {
if (keyContext && !strlen( keyContext ))
keyContext = NULL;
if (resultParam && !strlen( resultParam ))
resultParam = NULL;
MPSiteKey siteKey = mpw_siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
if (!siteKey)
return NULL;
trc( "-- mpw_siteResult (algorithm: %u)", algorithmVersion );
trc( "resultType: %d (%s)", resultType, mpw_nameForType( resultType ) );
trc( "resultParam: %s", resultParam );
char *sitePassword = NULL;
if (resultType & MPResultTypeClassTemplate) {
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_sitePasswordFromTemplate_v0( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion1:
return mpw_sitePasswordFromTemplate_v1( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion2:
return mpw_sitePasswordFromTemplate_v2( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion3:
return mpw_sitePasswordFromTemplate_v3( masterKey, siteKey, resultType, resultParam );
default:
err( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}
else if (resultType & MPResultTypeClassStateful) {
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion1:
return mpw_sitePasswordFromCrypt_v1( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion2:
return mpw_sitePasswordFromCrypt_v2( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion3:
return mpw_sitePasswordFromCrypt_v3( masterKey, siteKey, resultType, resultParam );
default:
err( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}
else if (resultType & MPResultTypeClassDerive) {
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_sitePasswordFromDerive_v0( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion1:
return mpw_sitePasswordFromDerive_v1( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion2:
return mpw_sitePasswordFromDerive_v2( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion3:
return mpw_sitePasswordFromDerive_v3( masterKey, siteKey, resultType, resultParam );
default:
err( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}
else {
err( "Unsupported password type: %d", resultType );
}
return sitePassword;
}
const char *mpw_siteState(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext,
const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion) {
if (keyContext && !strlen( keyContext ))
keyContext = NULL;
if (resultParam && !strlen( resultParam ))
resultParam = NULL;
MPSiteKey siteKey = mpw_siteKey( masterKey, siteName, siteCounter, keyPurpose, keyContext, algorithmVersion );
if (!siteKey)
return NULL;
trc( "-- mpw_siteState (algorithm: %u)", algorithmVersion );
trc( "resultType: %d (%s)", resultType, mpw_nameForType( resultType ) );
trc( "resultParam: %zu bytes = %s", sizeof( resultParam ), resultParam );
if (!masterKey || !resultParam)
return NULL;
switch (algorithmVersion) {
case MPAlgorithmVersion0:
return mpw_siteState_v0( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion1:
return mpw_siteState_v1( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion2:
return mpw_siteState_v2( masterKey, siteKey, resultType, resultParam );
case MPAlgorithmVersion3:
return mpw_siteState_v3( masterKey, siteKey, resultType, resultParam );
default:
err( "Unsupported version: %d", algorithmVersion );
return NULL;
}
}
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword) {
const char *leftArm[] = { "", "", "", "" };
const char *rightArm[] = { "", "", "", "" };
const char *body[] = { "", "", "", "", "", "" };
const char *accessory[] = {
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
const uint8_t *identiconSeed = NULL;
if (fullName && strlen( fullName ) && masterPassword && strlen( masterPassword ))
identiconSeed = mpw_hash_hmac_sha256(
(const uint8_t *)masterPassword, strlen( masterPassword ),
(const uint8_t *)fullName, strlen( fullName ) );
if (!identiconSeed)
return (MPIdenticon){
.leftArm = "",
.body = "",
.rightArm = "",
.accessory = "",
.color=0,
};
MPIdenticon identicon = {
.leftArm = leftArm[identiconSeed[0] % (sizeof( leftArm ) / sizeof( leftArm[0] ))],
.body = body[identiconSeed[1] % (sizeof( body ) / sizeof( body[0] ))],
.rightArm = rightArm[identiconSeed[2] % (sizeof( rightArm ) / sizeof( rightArm[0] ))],
.accessory = accessory[identiconSeed[3] % (sizeof( accessory ) / sizeof( accessory[0] ))],
.color = (uint8_t)(identiconSeed[4] % (MPIdenticonColorLast - MPIdenticonColorFirst + 1) + MPIdenticonColorFirst),
};
mpw_free( &identiconSeed, 32 );
return identicon;
}

View File

@@ -0,0 +1,72 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
// NOTE: mpw is currently NOT thread-safe.
#include "mpw-types.h"
#ifndef _MPW_ALGORITHM_H
#define _MPW_ALGORITHM_H
typedef mpw_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,
MPAlgorithmVersionCurrent = MPAlgorithmVersion3,
MPAlgorithmVersionFirst = MPAlgorithmVersion0,
MPAlgorithmVersionLast = MPAlgorithmVersion3,
};
/** Derive the master key for a user based on their name and master password.
* @return A new MPMasterKeySize-byte allocated buffer or NULL if an error occurred. */
MPMasterKey mpw_masterKey(
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
/** Derive the site key for a user's site from the given master key and site parameters.
* @return A new MPSiteKeySize-byte allocated buffer or NULL if an error occurred. */
MPSiteKey mpw_siteKey(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext, const MPAlgorithmVersion algorithmVersion);
/** Generate a site result token from the given parameters.
* @param resultParam A parameter for the resultType. For stateful result types, the output of mpw_siteState.
* @return A newly allocated string or NULL if an error occurred. */
const char *mpw_siteResult(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext,
const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion);
/** Encrypt a stateful site token for persistence.
* @param resultParam A parameter for the resultType. For stateful result types, the desired mpw_siteResult.
* @return A newly allocated string or NULL if an error occurred. */
const char *mpw_siteState(
MPMasterKey masterKey, const char *siteName, const MPCounterValue siteCounter,
const MPKeyPurpose keyPurpose, const char *keyContext,
const MPResultType resultType, const char *resultParam,
const MPAlgorithmVersion algorithmVersion);
/** @return A fingerprint for a user. */
MPIdenticon mpw_identicon(const char *fullName, const char *masterPassword);
#endif // _MPW_ALGORITHM_H

View File

@@ -0,0 +1,258 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include <errno.h>
#include <time.h>
#include "mpw-util.h"
#include "base64.h"
#define MP_N 32768LU
#define MP_r 8U
#define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// Algorithm version helpers.
static const char *mpw_templateForType_v0(MPResultType type, uint16_t templateIndex) {
size_t count = 0;
const char **templates = mpw_templatesForType( type, &count );
char const *template = templates && count? templates[templateIndex % count]: NULL;
free( templates );
return template;
}
static const char mpw_characterFromClass_v0(char characterClass, uint16_t classIndex) {
const char *classCharacters = mpw_charactersInClass( characterClass );
if (!classCharacters)
return '\0';
return classCharacters[classIndex % strlen( classCharacters )];
}
// Algorithm version overrides.
static MPMasterKey mpw_masterKey_v0(
const char *fullName, const char *masterPassword) {
const char *keyScope = mpw_scopeForPurpose( MPKeyPurposeAuthentication );
trc( "keyScope: %s", keyScope );
// Calculate the master key salt.
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( fullName ) ), fullName );
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope );
mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)mpw_utf8_strlen( fullName ) );
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
err( "Could not allocate master key salt: %s", strerror( errno ) );
return NULL;
}
trc( " => masterKeySalt.id: %s", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )", MP_N, MP_r, MP_p );
MPMasterKey masterKey = mpw_kdf_scrypt( MPMasterKeySize,
(uint8_t *)masterPassword, strlen( masterPassword ), masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( &masterKeySalt, masterKeySaltSize );
if (!masterKey) {
err( "Could not derive master key: %s", strerror( errno ) );
return NULL;
}
trc( " => masterKey.id: %s", mpw_id_buf( masterKey, MPMasterKeySize ) );
return masterKey;
}
static MPSiteKey mpw_siteKey_v0(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext) {
const char *keyScope = mpw_scopeForPurpose( keyPurpose );
trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter == MPCounterValueTOTP)
siteCounter = ((uint32_t)time( NULL ) / MP_otp_window) * MP_otp_window;
// Calculate the site seed.
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, mpw_hex_l( (uint32_t)mpw_utf8_strlen( siteName ) ), siteName, mpw_hex_l( siteCounter ),
keyContext? mpw_hex_l( (uint32_t)mpw_utf8_strlen( keyContext ) ): NULL, keyContext );
size_t siteSaltSize = 0;
uint8_t *siteSalt = NULL;
mpw_push_string( &siteSalt, &siteSaltSize, keyScope );
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strlen( siteName ) );
mpw_push_string( &siteSalt, &siteSaltSize, siteName );
mpw_push_int( &siteSalt, &siteSaltSize, siteCounter );
if (keyContext) {
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)mpw_utf8_strlen( keyContext ) );
mpw_push_string( &siteSalt, &siteSaltSize, keyContext );
}
if (!siteSalt) {
err( "Could not allocate site salt: %s", strerror( errno ) );
return NULL;
}
trc( " => siteSalt.id: %s", mpw_id_buf( siteSalt, siteSaltSize ) );
trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )",
mpw_id_buf( masterKey, MPMasterKeySize ) );
MPSiteKey siteKey = mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, siteSalt, siteSaltSize );
mpw_free( &siteSalt, siteSaltSize );
if (!siteKey) {
err( "Could not derive site key: %s", strerror( errno ) );
return NULL;
}
trc( " => siteKey.id: %s", mpw_id_buf( siteKey, MPSiteKeySize ) );
return siteKey;
}
static const char *mpw_sitePasswordFromTemplate_v0(
MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char __unused *resultParam) {
const char *_siteKey = (const char *)siteKey;
// Determine the template.
uint16_t seedByte;
mpw_uint16( (uint16_t)_siteKey[0], (uint8_t *)&seedByte );
const char *template = mpw_templateForType_v0( resultType, seedByte );
trc( "template: %u => %s", seedByte, template );
if (!template)
return NULL;
if (strlen( template ) > MPSiteKeySize) {
err( "Template too long for password seed: %zu", strlen( template ) );
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) {
mpw_uint16( (uint16_t)_siteKey[c + 1], (uint8_t *)&seedByte );
sitePassword[c] = mpw_characterFromClass_v0( template[c], seedByte );
trc( " - class: %c, index: %5u (0x%02hX) => character: %c",
template[c], seedByte, seedByte, sitePassword[c] );
}
trc( " => password: %s", sitePassword );
return sitePassword;
}
static const char *mpw_sitePasswordFromCrypt_v0(
MPMasterKey masterKey, MPSiteKey __unused siteKey, MPResultType __unused resultType, const char *cipherText) {
if (!cipherText) {
err( "Missing encrypted state." );
return NULL;
}
// Base64-decode
uint8_t *cipherBuf = calloc( 1, mpw_base64_decode_max( cipherText ) );
size_t bufSize = (size_t)mpw_base64_decode( cipherBuf, cipherText ), cipherBufSize = bufSize;
if ((int)bufSize < 0) {
err( "Base64 decoding error." );
mpw_free( &cipherBuf, mpw_base64_decode_max( cipherText ) );
return NULL;
}
trc( "b64 decoded: %zu bytes = %s", bufSize, mpw_hex( cipherBuf, bufSize ) );
// Decrypt
const uint8_t *plainBytes = mpw_aes_decrypt( masterKey, MPMasterKeySize, cipherBuf, &bufSize );
mpw_free( &cipherBuf, cipherBufSize );
const char *plainText = mpw_strndup( (char *)plainBytes, bufSize );
mpw_free( &plainBytes, bufSize );
if (!plainText)
err( "AES decryption error: %s", strerror( errno ) );
trc( "decrypted -> plainText: %zu bytes = %s = %s", strlen( plainText ), plainText, mpw_hex( plainText, strlen( plainText ) ) );
return plainText;
}
static const char *mpw_sitePasswordFromDerive_v0(
MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
switch (resultType) {
case MPResultTypeDeriveKey: {
if (!resultParam) {
err( "Missing key size parameter." );
return NULL;
}
int resultParamInt = atoi( resultParam );
if (!resultParamInt)
resultParamInt = 512;
if (resultParamInt < 128 || resultParamInt > 512 || resultParamInt % 8 != 0) {
err( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam );
return NULL;
}
uint16_t keySize = (uint16_t)(resultParamInt / 8);
trc( "keySize: %u", keySize );
// Derive key
const uint8_t *resultKey = mpw_kdf_blake2b( keySize, siteKey, MPSiteKeySize, NULL, 0, 0, NULL );
if (!resultKey) {
err( "Could not derive result key: %s", strerror( errno ) );
return NULL;
}
// Base64-encode
size_t b64Max = mpw_base64_encode_max( keySize );
char *b64Key = calloc( 1, b64Max + 1 );
if (mpw_base64_encode( b64Key, resultKey, keySize ) < 0) {
err( "Base64 encoding error." );
mpw_free_string( &b64Key );
}
else
trc( "b64 encoded -> key: %s", b64Key );
mpw_free( &resultKey, keySize );
return b64Key;
}
default:
err( "Unsupported derived password type: %d", resultType );
return NULL;
}
}
static const char *mpw_siteState_v0(
MPMasterKey masterKey, MPSiteKey __unused siteKey, MPResultType __unused resultType, const char *plainText) {
// Encrypt
size_t bufSize = strlen( plainText );
const uint8_t *cipherBuf = mpw_aes_encrypt( masterKey, MPMasterKeySize, (const uint8_t *)plainText, &bufSize );
if (!cipherBuf) {
err( "AES encryption error: %s", strerror( errno ) );
return NULL;
}
trc( "cipherBuf: %zu bytes = %s", bufSize, mpw_hex( cipherBuf, bufSize ) );
// Base64-encode
size_t b64Max = mpw_base64_encode_max( bufSize );
char *cipherText = calloc( 1, b64Max + 1 );
if (mpw_base64_encode( cipherText, cipherBuf, bufSize ) < 0) {
err( "Base64 encoding error." );
mpw_free_string( &cipherText );
}
else
trc( "b64 encoded -> cipherText: %s", cipherText );
mpw_free( &cipherBuf, bufSize );
return cipherText;
}

View File

@@ -0,0 +1,98 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include "mpw-util.h"
#define MP_N 32768LU
#define MP_r 8U
#define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// Inherited functions.
MPMasterKey mpw_masterKey_v0(
const char *fullName, const char *masterPassword);
MPSiteKey mpw_siteKey_v0(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext);
const char *mpw_sitePasswordFromCrypt_v0(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
const char *mpw_sitePasswordFromDerive_v0(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_siteState_v0(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
// Algorithm version overrides.
static MPMasterKey mpw_masterKey_v1(
const char *fullName, const char *masterPassword) {
return mpw_masterKey_v0( fullName, masterPassword );
}
static MPSiteKey mpw_siteKey_v1(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext) {
return mpw_siteKey_v0( masterKey, siteName, siteCounter, keyPurpose, keyContext );
}
static const char *mpw_sitePasswordFromTemplate_v1(
MPMasterKey __unused masterKey, MPSiteKey siteKey, MPResultType resultType, const char __unused *resultParam) {
// Determine the template.
uint8_t seedByte = siteKey[0];
const char *template = mpw_templateForType( resultType, seedByte );
trc( "template: %u => %s", seedByte, template );
if (!template)
return NULL;
if (strlen( template ) > MPSiteKeySize) {
err( "Template too long for password seed: %zu", strlen( template ) );
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) {
seedByte = siteKey[c + 1];
sitePassword[c] = mpw_characterFromClass( template[c], seedByte );
trc( " - class: %c, index: %3u (0x%02hhX) => character: %c",
template[c], seedByte, seedByte, sitePassword[c] );
}
trc( " => password: %s", sitePassword );
return sitePassword;
}
static const char *mpw_sitePasswordFromCrypt_v1(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
return mpw_sitePasswordFromCrypt_v0( masterKey, siteKey, resultType, cipherText );
}
static const char *mpw_sitePasswordFromDerive_v1(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromDerive_v0( masterKey, siteKey, resultType, resultParam );
}
static const char *mpw_siteState_v1(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
return mpw_siteState_v0( masterKey, siteKey, resultType, state );
}

View File

@@ -0,0 +1,115 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include <errno.h>
#include <time.h>
#include "mpw-util.h"
#define MP_N 32768LU
#define MP_r 8U
#define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// Inherited functions.
MPMasterKey mpw_masterKey_v1(
const char *fullName, const char *masterPassword);
const char *mpw_sitePasswordFromTemplate_v1(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_sitePasswordFromCrypt_v1(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
const char *mpw_sitePasswordFromDerive_v1(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_siteState_v1(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
// Algorithm version overrides.
static MPMasterKey mpw_masterKey_v2(
const char *fullName, const char *masterPassword) {
return mpw_masterKey_v1( fullName, masterPassword );
}
static MPSiteKey mpw_siteKey_v2(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext) {
const char *keyScope = mpw_scopeForPurpose( keyPurpose );
trc( "keyScope: %s", keyScope );
// OTP counter value.
if (siteCounter == MPCounterValueTOTP)
siteCounter = ((uint32_t)time( NULL ) / MP_otp_window) * MP_otp_window;
// Calculate the site seed.
trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | siteCounter=%s | #keyContext=%s | keyContext=%s",
keyScope, mpw_hex_l( (uint32_t)strlen( siteName ) ), siteName, mpw_hex_l( siteCounter ),
keyContext? mpw_hex_l( (uint32_t)strlen( keyContext ) ): NULL, keyContext );
size_t siteSaltSize = 0;
uint8_t *siteSalt = NULL;
mpw_push_string( &siteSalt, &siteSaltSize, keyScope );
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)strlen( siteName ) );
mpw_push_string( &siteSalt, &siteSaltSize, siteName );
mpw_push_int( &siteSalt, &siteSaltSize, siteCounter );
if (keyContext) {
mpw_push_int( &siteSalt, &siteSaltSize, (uint32_t)strlen( keyContext ) );
mpw_push_string( &siteSalt, &siteSaltSize, keyContext );
}
if (!siteSalt) {
err( "Could not allocate site salt: %s", strerror( errno ) );
return NULL;
}
trc( " => siteSalt.id: %s", mpw_id_buf( siteSalt, siteSaltSize ) );
trc( "siteKey: hmac-sha256( masterKey.id=%s, siteSalt )",
mpw_id_buf( masterKey, MPMasterKeySize ) );
MPSiteKey siteKey = mpw_hash_hmac_sha256( masterKey, MPMasterKeySize, siteSalt, siteSaltSize );
mpw_free( &siteSalt, siteSaltSize );
if (!siteKey) {
err( "Could not derive site key: %s", strerror( errno ) );
return NULL;
}
trc( " => siteKey.id: %s", mpw_id_buf( siteKey, MPSiteKeySize ) );
return siteKey;
}
static const char *mpw_sitePasswordFromTemplate_v2(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromTemplate_v1( masterKey, siteKey, resultType, resultParam );
}
static const char *mpw_sitePasswordFromCrypt_v2(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
return mpw_sitePasswordFromCrypt_v1( masterKey, siteKey, resultType, cipherText );
}
static const char *mpw_sitePasswordFromDerive_v2(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromDerive_v1( masterKey, siteKey, resultType, resultParam );
}
static const char *mpw_siteState_v2(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
return mpw_siteState_v1( masterKey, siteKey, resultType, state );
}

View File

@@ -0,0 +1,106 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include <errno.h>
#include "mpw-util.h"
#define MP_N 32768LU
#define MP_r 8U
#define MP_p 2U
#define MP_otp_window 5 * 60 /* s */
// Inherited functions.
MPSiteKey mpw_siteKey_v2(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext);
const char *mpw_sitePasswordFromTemplate_v2(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_sitePasswordFromCrypt_v2(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText);
const char *mpw_sitePasswordFromDerive_v2(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam);
const char *mpw_siteState_v2(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state);
// Algorithm version overrides.
static MPMasterKey mpw_masterKey_v3(
const char *fullName, const char *masterPassword) {
const char *keyScope = mpw_scopeForPurpose( MPKeyPurposeAuthentication );
trc( "keyScope: %s", keyScope );
// Calculate the master key salt.
trc( "masterKeySalt: keyScope=%s | #fullName=%s | fullName=%s",
keyScope, mpw_hex_l( (uint32_t)strlen( fullName ) ), fullName );
size_t masterKeySaltSize = 0;
uint8_t *masterKeySalt = NULL;
mpw_push_string( &masterKeySalt, &masterKeySaltSize, keyScope );
mpw_push_int( &masterKeySalt, &masterKeySaltSize, (uint32_t)strlen( fullName ) );
mpw_push_string( &masterKeySalt, &masterKeySaltSize, fullName );
if (!masterKeySalt) {
err( "Could not allocate master key salt: %s", strerror( errno ) );
return NULL;
}
trc( " => masterKeySalt.id: %s", mpw_id_buf( masterKeySalt, masterKeySaltSize ) );
// Calculate the master key.
trc( "masterKey: scrypt( masterPassword, masterKeySalt, N=%lu, r=%u, p=%u )", MP_N, MP_r, MP_p );
MPMasterKey masterKey = mpw_kdf_scrypt( MPMasterKeySize,
(uint8_t *)masterPassword, strlen( masterPassword ), masterKeySalt, masterKeySaltSize, MP_N, MP_r, MP_p );
mpw_free( &masterKeySalt, masterKeySaltSize );
if (!masterKey) {
err( "Could not derive master key: %s", strerror( errno ) );
return NULL;
}
trc( " => masterKey.id: %s", mpw_id_buf( masterKey, MPMasterKeySize ) );
return masterKey;
}
static MPSiteKey mpw_siteKey_v3(
MPMasterKey masterKey, const char *siteName, MPCounterValue siteCounter,
MPKeyPurpose keyPurpose, const char *keyContext) {
return mpw_siteKey_v2( masterKey, siteName, siteCounter, keyPurpose, keyContext );
}
static const char *mpw_sitePasswordFromTemplate_v3(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromTemplate_v2( masterKey, siteKey, resultType, resultParam );
}
static const char *mpw_sitePasswordFromCrypt_v3(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *cipherText) {
return mpw_sitePasswordFromCrypt_v2( masterKey, siteKey, resultType, cipherText );
}
static const char *mpw_sitePasswordFromDerive_v3(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *resultParam) {
return mpw_sitePasswordFromDerive_v2( masterKey, siteKey, resultType, resultParam );
}
static const char *mpw_siteState_v3(
MPMasterKey masterKey, MPSiteKey siteKey, MPResultType resultType, const char *state) {
return mpw_siteState_v2( masterKey, siteKey, resultType, state );
}

View File

@@ -0,0 +1,113 @@
#include <string.h>
#include "mpw-jni.h"
#include "mpw-algorithm.h"
#include "mpw-util.h"
// TODO: We may need to zero the jbytes safely.
/* native int _masterKey(final String fullName, final byte[] masterPassword, final Version version) */
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1masterKey(JNIEnv *env, jobject obj,
jstring fullName, jbyteArray masterPassword, jint algorithmVersion) {
const char *fullNameString = (*env)->GetStringUTFChars( env, fullName, NULL );
jbyte *masterPasswordString = (*env)->GetByteArrayElements( env, masterPassword, NULL );
MPMasterKey masterKeyBytes = mpw_masterKey( fullNameString, (char *)masterPasswordString, (MPAlgorithmVersion)algorithmVersion );
(*env)->ReleaseStringUTFChars( env, fullName, fullNameString );
(*env)->ReleaseByteArrayElements( env, masterPassword, masterPasswordString, JNI_ABORT );
if (!masterKeyBytes)
return NULL;
jbyteArray masterKey = (*env)->NewByteArray( env, (jsize)MPMasterKeySize );
(*env)->SetByteArrayRegion( env, masterKey, 0, (jsize)MPMasterKeySize, (jbyte *)masterKeyBytes );
mpw_free( &masterKeyBytes, MPMasterKeySize );
return masterKey;
}
/* native int _siteKey(final byte[] masterKey, final String siteName, final long siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext, final Version version) */
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteKey(JNIEnv *env, jobject obj,
jbyteArray masterKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext, jint algorithmVersion) {
jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL );
const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
MPMasterKey siteKeyBytes = mpw_siteKey(
(MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter,
(MPKeyPurpose)keyPurpose, keyContextString, (MPAlgorithmVersion)algorithmVersion );
(*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT );
(*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
(*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );
if (!siteKeyBytes)
return NULL;
jbyteArray siteKey = (*env)->NewByteArray( env, (jsize)MPMasterKeySize );
(*env)->SetByteArrayRegion( env, siteKey, 0, (jsize)MPMasterKeySize, (jbyte *)siteKeyBytes );
mpw_free( &siteKeyBytes, MPSiteKeySize );
return siteKey;
}
/* native String _siteResult(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
final MPResultType resultType, @Nullable final String resultParam, final Version version) */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteResult(JNIEnv *env, jobject obj,
jbyteArray masterKey, jbyteArray siteKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext,
jint resultType, jstring resultParam, jint algorithmVersion) {
jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL );
jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL );
const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
const char *resultParamString = resultParam? (*env)->GetStringUTFChars( env, resultParam, NULL ): NULL;
const char *siteResultString = mpw_siteResult(
(MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter,
(MPKeyPurpose)keyPurpose, keyContextString, (MPResultType)resultType, resultParamString, (MPAlgorithmVersion)algorithmVersion );
(*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT );
(*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT );
(*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
(*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );
(*env)->ReleaseStringUTFChars( env, resultParam, resultParamString );
if (!siteResultString)
return NULL;
jstring siteResult = (*env)->NewStringUTF( env, siteResultString );
mpw_free_string( &siteResultString );
return siteResult;
}
/* native String _siteState(final byte[] masterKey, final byte[] siteKey, final String siteName, final long siteCounter,
final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
final MPResultType resultType, final String resultParam, final Version version) */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteState(JNIEnv *env, jobject obj,
jbyteArray masterKey, jbyteArray siteKey, jstring siteName, jlong siteCounter, jint keyPurpose, jstring keyContext,
jint resultType, jstring resultParam, jint algorithmVersion) {
jbyte *masterKeyBytes = (*env)->GetByteArrayElements( env, masterKey, NULL );
jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL );
const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
const char *resultParamString = (*env)->GetStringUTFChars( env, resultParam, NULL );
const char *siteStateString = mpw_siteState(
(MPMasterKey)masterKeyBytes, siteNameString, (MPCounterValue)siteCounter,
(MPKeyPurpose)keyPurpose, keyContextString, (MPResultType)resultType, resultParamString, (MPAlgorithmVersion)algorithmVersion );
(*env)->ReleaseByteArrayElements( env, masterKey, masterKeyBytes, JNI_ABORT );
(*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT );
(*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
(*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );
(*env)->ReleaseStringUTFChars( env, resultParam, resultParamString );
if (!siteStateString)
return NULL;
jstring siteState = (*env)->NewStringUTF( env, siteStateString );
mpw_free_string( &siteStateString );
return siteState;
}

View File

@@ -0,0 +1,47 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lyndir_masterpassword_impl_MPAlgorithmV0 */
#ifndef _Included_com_lyndir_masterpassword_impl_MPAlgorithmV0
#define _Included_com_lyndir_masterpassword_impl_MPAlgorithmV0
#ifdef __cplusplus
extern "C" {
#endif
#undef com_lyndir_masterpassword_impl_MPAlgorithmV0_AES_BLOCKSIZE
#define com_lyndir_masterpassword_impl_MPAlgorithmV0_AES_BLOCKSIZE 128L
/*
* Class: com_lyndir_masterpassword_impl_MPAlgorithmV0
* Method: _masterKey
* Signature: (Ljava/lang/String;[BI)[B
*/
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1masterKey
(JNIEnv *, jobject, jstring, jbyteArray, jint);
/*
* Class: com_lyndir_masterpassword_impl_MPAlgorithmV0
* Method: _siteKey
* Signature: ([BLjava/lang/String;JILjava/lang/String;I)[B
*/
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteKey
(JNIEnv *, jobject, jbyteArray, jstring, jlong, jint, jstring, jint);
/*
* Class: com_lyndir_masterpassword_impl_MPAlgorithmV0
* Method: _siteResult
* Signature: ([B[BLjava/lang/String;JILjava/lang/String;ILjava/lang/String;I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteResult
(JNIEnv *, jobject, jbyteArray, jbyteArray, jstring, jlong, jint, jstring, jint, jstring, jint);
/*
* Class: com_lyndir_masterpassword_impl_MPAlgorithmV0
* Method: _siteState
* Signature: ([B[BLjava/lang/String;JILjava/lang/String;ILjava/lang/String;I)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_impl_MPAlgorithmV0__1siteState
(JNIEnv *, jobject, jbyteArray, jbyteArray, jstring, jlong, jint, jstring, jint, jstring, jint);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,116 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include "mpw-marshal-util.h"
#include "mpw-util.h"
char *mpw_get_token(const char **in, const char *eol, char *delim) {
// Skip leading spaces.
for (; **in == ' '; ++*in);
// Find characters up to the first delim.
size_t len = strcspn( *in, delim );
char *token = len && len <= (size_t)(eol - *in)? mpw_strndup( *in, len ): NULL;
// Advance past the delimitor.
*in = min( eol, *in + len + 1 );
return token;
}
time_t mpw_mktime(
const char *time) {
// TODO: Support parsing timezone into tm_gmtoff
struct tm tm = { .tm_isdst = -1 };
if (time && sscanf( time, "%4d-%2d-%2dT%2d:%2d:%2dZ",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) {
tm.tm_year -= 1900; // tm_year 0 = rfc3339 year 1900
tm.tm_mon -= 1; // tm_mon 0 = rfc3339 month 1
return mktime( &tm );
}
return false;
}
#if MPW_JSON
json_object *mpw_get_json_section(
json_object *obj, const char *section) {
json_object *json_value = obj;
char *sectionTokenizer = mpw_strdup( section ), *sectionToken = sectionTokenizer;
for (sectionToken = strtok( sectionToken, "." ); sectionToken; sectionToken = strtok( NULL, "." ))
if (!json_object_object_get_ex( json_value, sectionToken, &json_value ) || !json_value) {
trc( "While resolving: %s: Missing value for: %s", section, sectionToken );
json_value = NULL;
break;
}
free( sectionTokenizer );
return json_value;
}
const char *mpw_get_json_string(
json_object *obj, const char *section, const char *defaultValue) {
json_object *json_value = mpw_get_json_section( obj, section );
if (!json_value)
return defaultValue;
return json_object_get_string( json_value );
}
int64_t mpw_get_json_int(
json_object *obj, const char *section, int64_t defaultValue) {
json_object *json_value = mpw_get_json_section( obj, section );
if (!json_value)
return defaultValue;
return json_object_get_int64( json_value );
}
bool mpw_get_json_boolean(
json_object *obj, const char *section, bool defaultValue) {
json_object *json_value = mpw_get_json_section( obj, section );
if (!json_value)
return defaultValue;
return json_object_get_boolean( json_value ) == TRUE;
}
#endif
bool mpw_update_masterKey(MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm,
const char *fullName, const char *masterPassword) {
if (*masterKeyAlgorithm != targetKeyAlgorithm) {
mpw_free( masterKey, MPMasterKeySize );
*masterKeyAlgorithm = targetKeyAlgorithm;
*masterKey = mpw_masterKey( fullName, masterPassword, *masterKeyAlgorithm );
if (!*masterKey) {
err( "Couldn't derive master key for user %s, algorithm %d.", fullName, *masterKeyAlgorithm );
return false;
}
}
return true;
}

View File

@@ -0,0 +1,73 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#ifndef _MPW_MARSHAL_UTIL_H
#define _MPW_MARSHAL_UTIL_H
#include <time.h>
#if MPW_JSON
#include "json-c/json.h"
#endif
#include "mpw-algorithm.h"
/// Type parsing.
/** Get a token from a string by searching until the first character in delim, no farther than eol.
* The input string reference is advanced beyond the token delimitor if one is found.
* @return A new string containing the token or NULL if the delim wasn't found before eol. */
char *mpw_get_token(
const char **in, const char *eol, char *delim);
/** Convert an RFC 3339 time string into epoch time. */
time_t mpw_mktime(
const char *time);
/// JSON parsing.
#if MPW_JSON
/** Search for a JSON child object in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return A new JSON object or NULL if one of the section's object keys was not found in the source object's tree. */
json_object *mpw_get_json_section(
json_object *obj, const char *section);
/** Search for a string in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return A new string or defaultValue if one of the section's object keys was not found in the source object's tree. */
const char *mpw_get_json_string(
json_object *obj, const char *section, const char *defaultValue);
/** Search for an integer in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return The integer value or defaultValue if one of the section's object keys was not found in the source object's tree. */
int64_t mpw_get_json_int(
json_object *obj, const char *section, int64_t defaultValue);
/** Search for a boolean in a JSON object tree.
* @param section A dot-delimited list of JSON object keys to walk toward the child object.
* @return The boolean value or defaultValue if one of the section's object keys was not found in the source object's tree. */
bool mpw_get_json_boolean(
json_object *obj, const char *section, bool defaultValue);
#endif
/// mpw.
/** Calculate a master key if the target master key algorithm is different from the given master key algorithm.
* @return false if an error occurred during the derivation of the master key. */
bool mpw_update_masterKey(
MPMasterKey *masterKey, MPAlgorithmVersion *masterKeyAlgorithm, MPAlgorithmVersion targetKeyAlgorithm,
const char *fullName, const char *masterPassword);
#endif // _MPW_MARSHAL_UTIL_H

View File

@@ -0,0 +1,924 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "mpw-marshal.h"
#include "mpw-util.h"
#include "mpw-marshal-util.h"
MPMarshalledUser *mpw_marshal_user(
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion) {
MPMarshalledUser *user;
if (!fullName || !masterPassword || !(user = malloc( sizeof( MPMarshalledUser ) )))
return NULL;
*user = (MPMarshalledUser){
.fullName = mpw_strdup( fullName ),
.masterPassword = mpw_strdup( masterPassword ),
.algorithm = algorithmVersion,
.redacted = true,
.avatar = 0,
.defaultType = MPResultTypeDefault,
.lastUsed = 0,
.sites_count = 0,
.sites = NULL,
};
return user;
}
MPMarshalledSite *mpw_marshal_site(
MPMarshalledUser *user, const char *siteName, const MPResultType resultType,
const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion) {
if (!siteName || !mpw_realloc( &user->sites, NULL, sizeof( MPMarshalledSite ) * ++user->sites_count ))
return NULL;
MPMarshalledSite *site = &user->sites[user->sites_count - 1];
*site = (MPMarshalledSite){
.name = mpw_strdup( siteName ),
.content = NULL,
.type = resultType,
.counter = siteCounter,
.algorithm = algorithmVersion,
.loginContent = NULL,
.loginType = MPResultTypeTemplateName,
.url = NULL,
.uses = 0,
.lastUsed = 0,
.questions_count = 0,
.questions = NULL,
};
return site;
}
MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword) {
if (!mpw_realloc( &site->questions, NULL, sizeof( MPMarshalledQuestion ) * ++site->questions_count ))
return NULL;
if (!keyword)
keyword = "";
MPMarshalledQuestion *question = &site->questions[site->questions_count - 1];
*question = (MPMarshalledQuestion){
.keyword = mpw_strdup( keyword ),
.content = NULL,
.type = MPResultTypeTemplatePhrase,
};
return question;
}
bool mpw_marshal_info_free(
MPMarshalInfo **info) {
if (!info || !*info)
return true;
bool success = true;
success &= mpw_free_strings( &(*info)->fullName, &(*info)->keyID, NULL );
success &= mpw_free( info, sizeof( MPMarshalInfo ) );
return success;
}
bool mpw_marshal_free(
MPMarshalledUser **user) {
if (!user || !*user)
return true;
bool success = true;
success &= mpw_free_strings( &(*user)->fullName, &(*user)->masterPassword, NULL );
for (size_t s = 0; s < (*user)->sites_count; ++s) {
MPMarshalledSite *site = &(*user)->sites[s];
success &= mpw_free_strings( &site->name, &site->content, &site->loginContent, &site->url, NULL );
for (size_t q = 0; q < site->questions_count; ++q) {
MPMarshalledQuestion *question = &site->questions[q];
success &= mpw_free_strings( &question->keyword, &question->content, NULL );
}
success &= mpw_free( &site->questions, sizeof( MPMarshalledQuestion ) * site->questions_count );
}
success &= mpw_free( &(*user)->sites, sizeof( MPMarshalledSite ) * (*user)->sites_count );
success &= mpw_free( user, sizeof( MPMarshalledUser ) );
return success;
}
static bool mpw_marshal_write_flat(
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return false;
}
if (!user->masterPassword || !strlen( user->masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Missing master password." };
return false;
}
MPMasterKey masterKey = NULL;
MPAlgorithmVersion masterKeyAlgorithm = user->algorithm - 1;
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user->algorithm, user->fullName, user->masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false;
}
mpw_string_pushf( out, "# Master Password site export\n" );
if (user->redacted)
mpw_string_pushf( out, "# Export of site names and stored passwords (unless device-private) encrypted with the master key.\n" );
else
mpw_string_pushf( out, "# Export of site names and passwords in clear-text.\n" );
mpw_string_pushf( out, "# \n" );
mpw_string_pushf( out, "##\n" );
mpw_string_pushf( out, "# Format: %d\n", 1 );
char dateString[21];
time_t now = time( NULL );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &now ) ))
mpw_string_pushf( out, "# Date: %s\n", dateString );
mpw_string_pushf( out, "# User Name: %s\n", user->fullName );
mpw_string_pushf( out, "# Full Name: %s\n", user->fullName );
mpw_string_pushf( out, "# Avatar: %u\n", user->avatar );
mpw_string_pushf( out, "# Key ID: %s\n", mpw_id_buf( masterKey, MPMasterKeySize ) );
mpw_string_pushf( out, "# Algorithm: %d\n", user->algorithm );
mpw_string_pushf( out, "# Default Type: %d\n", user->defaultType );
mpw_string_pushf( out, "# Passwords: %s\n", user->redacted? "PROTECTED": "VISIBLE" );
mpw_string_pushf( out, "##\n" );
mpw_string_pushf( out, "#\n" );
mpw_string_pushf( out, "# Last Times Password Login\t Site\tSite\n" );
mpw_string_pushf( out, "# used used type name\t name\tpassword\n" );
// Sites.
for (size_t s = 0; s < user->sites_count; ++s) {
MPMarshalledSite *site = &user->sites[s];
if (!site->name || !strlen( site->name ))
continue;
const char *content = NULL, *loginContent = NULL;
if (!user->redacted) {
// Clear Text
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, user->fullName, user->masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false;
}
content = mpw_siteResult( masterKey, site->name, site->counter,
MPKeyPurposeAuthentication, NULL, site->type, site->content, site->algorithm );
loginContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeIdentification, NULL, site->loginType, site->loginContent, site->algorithm );
}
else {
// Redacted
if (site->type & MPSiteFeatureExportContent && site->content && strlen( site->content ))
content = mpw_strdup( site->content );
if (site->loginType & MPSiteFeatureExportContent && site->loginContent && strlen( site->loginContent ))
loginContent = mpw_strdup( site->loginContent );
}
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
mpw_string_pushf( out, "%s %8ld %lu:%lu:%lu %25s\t%25s\t%s\n",
dateString, (long)site->uses, (long)site->type, (long)site->algorithm, (long)site->counter,
loginContent?: "", site->name, content?: "" );
mpw_free_strings( &content, &loginContent, NULL );
}
mpw_free( &masterKey, MPMasterKeySize );
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return true;
}
#if MPW_JSON
static bool mpw_marshal_write_json(
char **out, const MPMarshalledUser *user, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!user->fullName || !strlen( user->fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing full name." };
return false;
}
if (!user->masterPassword || !strlen( user->masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Missing master password." };
return false;
}
MPMasterKey masterKey = NULL;
MPAlgorithmVersion masterKeyAlgorithm = user->algorithm - 1;
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, user->algorithm, user->fullName, user->masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false;
}
// Section: "export"
json_object *json_file = json_object_new_object();
json_object *json_export = json_object_new_object();
json_object_object_add( json_file, "export", json_export );
json_object_object_add( json_export, "format", json_object_new_int( 1 ) );
json_object_object_add( json_export, "redacted", json_object_new_boolean( user->redacted ) );
char dateString[21];
time_t now = time( NULL );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &now ) ))
json_object_object_add( json_export, "date", json_object_new_string( dateString ) );
// Section: "user"
json_object *json_user = json_object_new_object();
json_object_object_add( json_file, "user", json_user );
json_object_object_add( json_user, "avatar", json_object_new_int( (int32_t)user->avatar ) );
json_object_object_add( json_user, "full_name", json_object_new_string( user->fullName ) );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &user->lastUsed ) ))
json_object_object_add( json_user, "last_used", json_object_new_string( dateString ) );
json_object_object_add( json_user, "key_id", json_object_new_string( mpw_id_buf( masterKey, MPMasterKeySize ) ) );
json_object_object_add( json_user, "algorithm", json_object_new_int( (int32_t)user->algorithm ) );
json_object_object_add( json_user, "default_type", json_object_new_int( (int32_t)user->defaultType ) );
// Section "sites"
json_object *json_sites = json_object_new_object();
json_object_object_add( json_file, "sites", json_sites );
for (size_t s = 0; s < user->sites_count; ++s) {
MPMarshalledSite *site = &user->sites[s];
if (!site->name || !strlen( site->name ))
continue;
const char *content = NULL, *loginContent = NULL;
if (!user->redacted) {
// Clear Text
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, user->fullName, user->masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return false;
}
content = mpw_siteResult( masterKey, site->name, site->counter,
MPKeyPurposeAuthentication, NULL, site->type, site->content, site->algorithm );
loginContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeIdentification, NULL, site->loginType, site->loginContent, site->algorithm );
}
else {
// Redacted
if (site->type & MPSiteFeatureExportContent && site->content && strlen( site->content ))
content = mpw_strdup( site->content );
if (site->loginType & MPSiteFeatureExportContent && site->loginContent && strlen( site->loginContent ))
loginContent = mpw_strdup( site->loginContent );
}
json_object *json_site = json_object_new_object();
json_object_object_add( json_sites, site->name, json_site );
json_object_object_add( json_site, "type", json_object_new_int( (int32_t)site->type ) );
json_object_object_add( json_site, "counter", json_object_new_int( (int32_t)site->counter ) );
json_object_object_add( json_site, "algorithm", json_object_new_int( (int32_t)site->algorithm ) );
if (content)
json_object_object_add( json_site, "password", json_object_new_string( content ) );
if (loginContent)
json_object_object_add( json_site, "login_name", json_object_new_string( loginContent ) );
json_object_object_add( json_site, "login_type", json_object_new_int( (int32_t)site->loginType ) );
json_object_object_add( json_site, "uses", json_object_new_int( (int32_t)site->uses ) );
if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
json_object_object_add( json_site, "last_used", json_object_new_string( dateString ) );
json_object *json_site_questions = json_object_new_object();
json_object_object_add( json_site, "questions", json_site_questions );
for (size_t q = 0; q < site->questions_count; ++q) {
MPMarshalledQuestion *question = &site->questions[q];
if (!question->keyword)
continue;
json_object *json_site_question = json_object_new_object();
json_object_object_add( json_site_questions, question->keyword, json_site_question );
json_object_object_add( json_site_question, "type", json_object_new_int( (int32_t)question->type ) );
if (!user->redacted) {
// Clear Text
const char *answerContent = mpw_siteResult( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, question->content, site->algorithm );
json_object_object_add( json_site_question, "answer", json_object_new_string( answerContent ) );
}
else {
// Redacted
if (site->type & MPSiteFeatureExportContent && question->content && strlen( question->content ))
json_object_object_add( json_site_question, "answer", json_object_new_string( question->content ) );
}
}
json_object *json_site_mpw = json_object_new_object();
json_object_object_add( json_site, "_ext_mpw", json_site_mpw );
if (site->url)
json_object_object_add( json_site_mpw, "url", json_object_new_string( site->url ) );
mpw_free_strings( &content, &loginContent, NULL );
}
mpw_string_pushf( out, "%s\n", json_object_to_json_string_ext( json_file, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED ) );
mpw_free( &masterKey, MPMasterKeySize );
json_object_put( json_file );
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return true;
}
#endif
bool mpw_marshal_write(
char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error) {
switch (outFormat) {
case MPMarshalFormatNone:
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return false;
case MPMarshalFormatFlat:
return mpw_marshal_write_flat( out, user, error );
#if MPW_JSON
case MPMarshalFormatJSON:
return mpw_marshal_write_json( out, user, error );
#endif
default:
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported output format: %u", outFormat ) };
return false;
}
}
static void mpw_marshal_read_flat_info(
const char *in, MPMarshalInfo *info) {
info->algorithm = MPAlgorithmVersionCurrent;
// Parse import data.
bool headerStarted = false;
for (const char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) {
// Comment or header
if (*positionInLine == '#') {
++positionInLine;
if (!headerStarted) {
if (*positionInLine == '#')
// ## starts header
headerStarted = true;
// Comment before header
continue;
}
if (*positionInLine == '#')
// ## ends header
break;
// Header
char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
if (!headerName || !headerValue)
continue;
if (strcmp( headerName, "Algorithm" ) == 0)
info->algorithm = (MPAlgorithmVersion)atoi( headerValue );
if (strcmp( headerName, "Full Name" ) == 0 || strcmp( headerName, "User Name" ) == 0)
info->fullName = mpw_strdup( headerValue );
if (strcmp( headerName, "Key ID" ) == 0)
info->keyID = mpw_strdup( headerValue );
if (strcmp( headerName, "Passwords" ) == 0)
info->redacted = strcmp( headerValue, "VISIBLE" ) != 0;
if (strcmp( headerName, "Date" ) == 0)
info->date = mpw_mktime( headerValue );
mpw_free_strings( &headerName, &headerValue, NULL );
continue;
}
}
}
static MPMarshalledUser *mpw_marshal_read_flat(
const char *in, const char *masterPassword, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!in || !strlen( in )) {
error->type = MPMarshalErrorStructure;
error->description = mpw_str( "No input data." );
return NULL;
}
// Parse import data.
MPMasterKey masterKey = NULL;
MPMarshalledUser *user = NULL;
unsigned int format = 0, avatar = 0;
char *fullName = NULL, *keyID = NULL;
MPAlgorithmVersion algorithm = MPAlgorithmVersionCurrent, masterKeyAlgorithm = (MPAlgorithmVersion)-1;
MPResultType defaultType = MPResultTypeDefault;
bool headerStarted = false, headerEnded = false, importRedacted = false;
for (const char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) {
// Comment or header
if (*positionInLine == '#') {
++positionInLine;
if (!headerStarted) {
if (*positionInLine == '#')
// ## starts header
headerStarted = true;
// Comment before header
continue;
}
if (headerEnded)
// Comment after header
continue;
if (*positionInLine == '#') {
// ## ends header
headerEnded = true;
continue;
}
// Header
char *headerName = mpw_get_token( &positionInLine, endOfLine, ":\n" );
char *headerValue = mpw_get_token( &positionInLine, endOfLine, "\n" );
if (!headerName || !headerValue) {
error->type = MPMarshalErrorStructure;
error->description = mpw_str( "Invalid header: %s", mpw_strndup( positionInLine, (size_t)(endOfLine - positionInLine) ) );
return NULL;
}
if (strcmp( headerName, "Format" ) == 0)
format = (unsigned int)atoi( headerValue );
if (strcmp( headerName, "Full Name" ) == 0 || strcmp( headerName, "User Name" ) == 0)
fullName = mpw_strdup( headerValue );
if (strcmp( headerName, "Avatar" ) == 0)
avatar = (unsigned int)atoi( headerValue );
if (strcmp( headerName, "Key ID" ) == 0)
keyID = mpw_strdup( headerValue );
if (strcmp( headerName, "Algorithm" ) == 0) {
int value = atoi( headerValue );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm version: %s", headerValue ) };
return NULL;
}
algorithm = (MPAlgorithmVersion)value;
}
if (strcmp( headerName, "Default Type" ) == 0) {
int value = atoi( headerValue );
if (!mpw_nameForType( (MPResultType)value )) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %s", headerValue ) };
return NULL;
}
defaultType = (MPResultType)value;
}
if (strcmp( headerName, "Passwords" ) == 0)
importRedacted = strcmp( headerValue, "VISIBLE" ) != 0;
mpw_free_strings( &headerName, &headerValue, NULL );
continue;
}
if (!headerEnded)
continue;
if (!fullName) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing header: Full Name" };
return NULL;
}
if (positionInLine >= endOfLine)
continue;
if (!user) {
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return NULL;
}
if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master password doesn't match key ID." };
return NULL;
}
if (!(user = mpw_marshal_user( fullName, masterPassword, algorithm ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." };
return NULL;
}
user->redacted = importRedacted;
user->avatar = avatar;
user->defaultType = defaultType;
}
// Site
char *siteLoginName = NULL, *siteName = NULL, *siteContent = NULL;
char *str_lastUsed = NULL, *str_uses = NULL, *str_type = NULL, *str_algorithm = NULL, *str_counter = NULL;
switch (format) {
case 0: {
str_lastUsed = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
str_uses = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
char *typeAndVersion = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
if (typeAndVersion) {
str_type = mpw_strdup( strtok( typeAndVersion, ":" ) );
str_algorithm = mpw_strdup( strtok( NULL, "" ) );
mpw_free_string( &typeAndVersion );
}
str_counter = mpw_strdup( "1" );
siteLoginName = NULL;
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteContent = mpw_get_token( &positionInLine, endOfLine, "\n" );
break;
}
case 1: {
str_lastUsed = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
str_uses = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
char *typeAndVersionAndCounter = mpw_get_token( &positionInLine, endOfLine, " \t\n" );
if (typeAndVersionAndCounter) {
str_type = mpw_strdup( strtok( typeAndVersionAndCounter, ":" ) );
str_algorithm = mpw_strdup( strtok( NULL, ":" ) );
str_counter = mpw_strdup( strtok( NULL, "" ) );
mpw_free_string( &typeAndVersionAndCounter );
}
siteLoginName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteName = mpw_get_token( &positionInLine, endOfLine, "\t\n" );
siteContent = mpw_get_token( &positionInLine, endOfLine, "\n" );
break;
}
default: {
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unexpected import format: %u", format ) };
return NULL;
}
}
if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
MPResultType siteType = (MPResultType)atoi( str_type );
if (!mpw_nameForType( siteType )) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %s", siteName, str_type ) };
return NULL;
}
long long int value = atoll( str_counter );
if (value < MPCounterValueFirst || value > MPCounterValueLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %s", siteName, str_counter ) };
return NULL;
}
MPCounterValue siteCounter = (MPCounterValue)value;
value = atoll( str_algorithm );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm: %s: %s", siteName, str_algorithm ) };
return NULL;
}
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
time_t siteLastUsed = mpw_mktime( str_lastUsed );
if (!siteLastUsed) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
return NULL;
}
MPMarshalledSite *site = mpw_marshal_site(
user, siteName, siteType, siteCounter, siteAlgorithm );
if (!site) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." };
return NULL;
}
site->uses = (unsigned int)atoi( str_uses );
site->lastUsed = siteLastUsed;
if (!user->redacted) {
// Clear Text
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return NULL;
}
if (siteContent && strlen( siteContent ))
site->content = mpw_siteState( masterKey, site->name, site->counter,
MPKeyPurposeAuthentication, NULL, site->type, siteContent, site->algorithm );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeIdentification, NULL, site->loginType, siteLoginName, site->algorithm );
}
else {
// Redacted
if (siteContent && strlen( siteContent ))
site->content = mpw_strdup( siteContent );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = mpw_strdup( siteLoginName );
}
}
else {
error->type = MPMarshalErrorMissing;
error->description = mpw_str(
"Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s",
str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginName, siteName );
return NULL;
}
mpw_free_strings( &str_lastUsed, &str_uses, &str_type, &str_algorithm, &str_counter, NULL );
mpw_free_strings( &siteLoginName, &siteName, &siteContent, NULL );
}
mpw_free_strings( &fullName, &keyID, NULL );
mpw_free( &masterKey, MPMasterKeySize );
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return user;
}
#if MPW_JSON
static void mpw_marshal_read_json_info(
const char *in, MPMarshalInfo *info) {
// Parse JSON.
enum json_tokener_error json_error = json_tokener_success;
json_object *json_file = json_tokener_parse_verbose( in, &json_error );
if (!json_file || json_error != json_tokener_success)
return;
// Section: "export"
int64_t fileFormat = mpw_get_json_int( json_file, "export.format", 0 );
if (fileFormat < 1)
return;
info->redacted = mpw_get_json_boolean( json_file, "export.redacted", true );
info->date = mpw_mktime( mpw_get_json_string( json_file, "export.date", NULL ) );
// Section: "user"
info->algorithm = (MPAlgorithmVersion)mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent );
info->fullName = mpw_strdup( mpw_get_json_string( json_file, "user.full_name", NULL ) );
info->keyID = mpw_strdup( mpw_get_json_string( json_file, "user.key_id", NULL ) );
json_object_put( json_file );
}
static MPMarshalledUser *mpw_marshal_read_json(
const char *in, const char *masterPassword, MPMarshalError *error) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Unexpected internal error." };
if (!in || !strlen( in )) {
error->type = MPMarshalErrorStructure;
error->description = mpw_str( "No input data." );
return NULL;
}
// Parse JSON.
enum json_tokener_error json_error = json_tokener_success;
json_object *json_file = json_tokener_parse_verbose( in, &json_error );
if (!json_file || json_error != json_tokener_success) {
*error = (MPMarshalError){ MPMarshalErrorStructure, mpw_str( "JSON error: %s", json_tokener_error_desc( json_error ) ) };
return NULL;
}
// Parse import data.
MPMasterKey masterKey = NULL;
MPAlgorithmVersion masterKeyAlgorithm = (MPAlgorithmVersion)-1;
MPMarshalledUser *user = NULL;
// Section: "export"
int64_t fileFormat = mpw_get_json_int( json_file, "export.format", 0 );
if (fileFormat < 1) {
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported format: %u", fileFormat ) };
return NULL;
}
bool fileRedacted = mpw_get_json_boolean( json_file, "export.redacted", true );
// Section: "user"
unsigned int avatar = (unsigned int)mpw_get_json_int( json_file, "user.avatar", 0 );
const char *fullName = mpw_get_json_string( json_file, "user.full_name", NULL );
const char *str_lastUsed = mpw_get_json_string( json_file, "user.last_used", NULL );
const char *keyID = mpw_get_json_string( json_file, "user.key_id", NULL );
int64_t value = mpw_get_json_int( json_file, "user.algorithm", MPAlgorithmVersionCurrent );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user algorithm version: %u", value ) };
return NULL;
}
MPAlgorithmVersion algorithm = (MPAlgorithmVersion)value;
MPResultType defaultType = (MPResultType)mpw_get_json_int( json_file, "user.default_type", MPResultTypeDefault );
if (!mpw_nameForType( defaultType )) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user default type: %u", defaultType ) };
return NULL;
}
time_t lastUsed = mpw_mktime( str_lastUsed );
if (!lastUsed) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid user last used: %s", str_lastUsed ) };
return NULL;
}
if (!fullName || !strlen( fullName )) {
*error = (MPMarshalError){ MPMarshalErrorMissing, "Missing value for full name." };
return NULL;
}
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, algorithm, fullName, masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return NULL;
}
if (keyID && !mpw_id_buf_equals( keyID, mpw_id_buf( masterKey, MPMasterKeySize ) )) {
*error = (MPMarshalError){ MPMarshalErrorMasterPassword, "Master password doesn't match key ID." };
return NULL;
}
if (!(user = mpw_marshal_user( fullName, masterPassword, algorithm ))) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new user." };
return NULL;
}
user->redacted = fileRedacted;
user->avatar = avatar;
user->defaultType = defaultType;
user->lastUsed = lastUsed;
// Section "sites"
json_object_iter json_site;
json_object *json_sites = mpw_get_json_section( json_file, "sites" );
json_object_object_foreachC( json_sites, json_site ) {
const char *siteName = json_site.key;
value = mpw_get_json_int( json_site.val, "algorithm", (int32_t)user->algorithm );
if (value < MPAlgorithmVersionFirst || value > MPAlgorithmVersionLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site algorithm version: %s: %d", siteName, value ) };
return NULL;
}
MPAlgorithmVersion siteAlgorithm = (MPAlgorithmVersion)value;
MPResultType siteType = (MPResultType)mpw_get_json_int( json_site.val, "type", (int32_t)user->defaultType );
if (!mpw_nameForType( siteType )) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site type: %s: %u", siteName, siteType ) };
return NULL;
}
value = mpw_get_json_int( json_site.val, "counter", 1 );
if (value < MPCounterValueFirst || value > MPCounterValueLast) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site counter: %s: %d", siteName, value ) };
return NULL;
}
MPCounterValue siteCounter = (MPCounterValue)value;
const char *siteContent = mpw_get_json_string( json_site.val, "password", NULL );
const char *siteLoginName = mpw_get_json_string( json_site.val, "login_name", NULL );
MPResultType siteLoginType = (MPResultType)mpw_get_json_int( json_site.val, "login_type", MPResultTypeTemplateName );
unsigned int siteUses = (unsigned int)mpw_get_json_int( json_site.val, "uses", 0 );
str_lastUsed = mpw_get_json_string( json_site.val, "last_used", NULL );
time_t siteLastUsed = mpw_mktime( str_lastUsed );
if (!siteLastUsed) {
*error = (MPMarshalError){ MPMarshalErrorIllegal, mpw_str( "Invalid site last used: %s: %s", siteName, str_lastUsed ) };
return NULL;
}
json_object *json_site_mpw = mpw_get_json_section( json_site.val, "_ext_mpw" );
const char *siteURL = mpw_get_json_string( json_site_mpw, "url", NULL );
MPMarshalledSite *site = mpw_marshal_site( user, siteName, siteType, siteCounter, siteAlgorithm );
if (!site) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't allocate a new site." };
return NULL;
}
site->loginType = siteLoginType;
site->url = siteURL? mpw_strdup( siteURL ): NULL;
site->uses = siteUses;
site->lastUsed = siteLastUsed;
if (!user->redacted) {
// Clear Text
if (!mpw_update_masterKey( &masterKey, &masterKeyAlgorithm, site->algorithm, fullName, masterPassword )) {
*error = (MPMarshalError){ MPMarshalErrorInternal, "Couldn't derive master key." };
return NULL;
}
if (siteContent && strlen( siteContent ))
site->content = mpw_siteState( masterKey, site->name, site->counter,
MPKeyPurposeAuthentication, NULL, site->type, siteContent, site->algorithm );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeIdentification, NULL, site->loginType, siteLoginName, site->algorithm );
}
else {
// Redacted
if (siteContent && strlen( siteContent ))
site->content = mpw_strdup( siteContent );
if (siteLoginName && strlen( siteLoginName ))
site->loginContent = mpw_strdup( siteLoginName );
}
json_object_iter json_site_question;
json_object *json_site_questions = mpw_get_json_section( json_site.val, "questions" );
json_object_object_foreachC( json_site_questions, json_site_question ) {
MPMarshalledQuestion *question = mpw_marshal_question( site, json_site_question.key );
const char *answerContent = mpw_get_json_string( json_site_question.val, "answer", NULL );
question->type = (MPResultType)mpw_get_json_int( json_site_question.val, "type", MPResultTypeTemplatePhrase );
if (!user->redacted) {
// Clear Text
if (answerContent && strlen( answerContent ))
question->content = mpw_siteState( masterKey, site->name, MPCounterValueInitial,
MPKeyPurposeRecovery, question->keyword, question->type, answerContent, site->algorithm );
}
else {
// Redacted
if (answerContent && strlen( answerContent ))
question->content = mpw_strdup( answerContent );
}
}
}
json_object_put( json_file );
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return user;
}
#endif
MPMarshalInfo *mpw_marshal_read_info(
const char *in) {
MPMarshalInfo *info = malloc( sizeof( MPMarshalInfo ) );
*info = (MPMarshalInfo){ .format = MPMarshalFormatNone };
if (in && strlen( in )) {
if (in[0] == '#') {
*info = (MPMarshalInfo){ .format = MPMarshalFormatFlat };
mpw_marshal_read_flat_info( in, info );
}
else if (in[0] == '{') {
*info = (MPMarshalInfo){ .format = MPMarshalFormatJSON };
#if MPW_JSON
mpw_marshal_read_json_info( in, info );
#endif
}
}
return info;
}
MPMarshalledUser *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, const char *masterPassword, MPMarshalError *error) {
switch (inFormat) {
case MPMarshalFormatNone:
*error = (MPMarshalError){ .type = MPMarshalSuccess };
return false;
case MPMarshalFormatFlat:
return mpw_marshal_read_flat( in, masterPassword, error );
#if MPW_JSON
case MPMarshalFormatJSON:
return mpw_marshal_read_json( in, masterPassword, error );
#endif
default:
*error = (MPMarshalError){ MPMarshalErrorFormat, mpw_str( "Unsupported input format: %u", inFormat ) };
return NULL;
}
}
const MPMarshalFormat mpw_formatWithName(
const char *formatName) {
if (!formatName || !strlen( formatName ))
return MPMarshalFormatNone;
// Lower-case to standardize it.
size_t stdFormatNameSize = strlen( formatName );
char stdFormatName[stdFormatNameSize + 1];
for (size_t c = 0; c < stdFormatNameSize; ++c)
stdFormatName[c] = (char)tolower( formatName[c] );
stdFormatName[stdFormatNameSize] = '\0';
if (strncmp( mpw_nameForFormat( MPMarshalFormatNone ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshalFormatNone;
if (strncmp( mpw_nameForFormat( MPMarshalFormatFlat ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshalFormatFlat;
if (strncmp( mpw_nameForFormat( MPMarshalFormatJSON ), stdFormatName, strlen( stdFormatName ) ) == 0)
return MPMarshalFormatJSON;
dbg( "Not a format name: %s", stdFormatName );
return (MPMarshalFormat)ERR;
}
const char *mpw_nameForFormat(
const MPMarshalFormat format) {
switch (format) {
case MPMarshalFormatNone:
return "none";
case MPMarshalFormatFlat:
return "flat";
case MPMarshalFormatJSON:
return "json";
default: {
dbg( "Unknown format: %d", format );
return NULL;
}
}
}
const char *mpw_marshal_format_extension(
const MPMarshalFormat format) {
switch (format) {
case MPMarshalFormatNone:
return NULL;
case MPMarshalFormatFlat:
return "mpsites";
case MPMarshalFormatJSON:
return "mpsites.json";
default: {
dbg( "Unknown format: %d", format );
return NULL;
}
}
}

View File

@@ -0,0 +1,159 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#ifndef _MPW_MARSHAL_H
#define _MPW_MARSHAL_H
#include <time.h>
#include "mpw-algorithm.h"
//// Types.
typedef mpw_enum( unsigned int, MPMarshalFormat ) {
/** Do not marshal. */
MPMarshalFormatNone,
/** Marshal using the line-based plain-text format. */
MPMarshalFormatFlat,
/** Marshal using the JSON structured format. */
MPMarshalFormatJSON,
#if MPW_JSON
MPMarshalFormatDefault = MPMarshalFormatJSON,
#else
MPMarshalFormatDefault = MPMarshalFormatFlat,
#endif
};
typedef mpw_enum( unsigned int, MPMarshalErrorType ) {
/** The marshalling operation completed successfully. */
MPMarshalSuccess,
/** An error in the structure of the marshall file interrupted marshalling. */
MPMarshalErrorStructure,
/** The marshall file uses an unsupported format version. */
MPMarshalErrorFormat,
/** A required value is missing or not specified. */
MPMarshalErrorMissing,
/** The given master password is not valid. */
MPMarshalErrorMasterPassword,
/** An illegal value was specified. */
MPMarshalErrorIllegal,
/** An internal system error interrupted marshalling. */
MPMarshalErrorInternal,
};
typedef struct MPMarshalError {
MPMarshalErrorType type;
const char *description;
} MPMarshalError;
typedef struct MPMarshalledQuestion {
const char *keyword;
const char *content;
MPResultType type;
} MPMarshalledQuestion;
typedef struct MPMarshalledSite {
const char *name;
const char *content;
MPResultType type;
MPCounterValue counter;
MPAlgorithmVersion algorithm;
const char *loginContent;
MPResultType loginType;
const char *url;
unsigned int uses;
time_t lastUsed;
size_t questions_count;
MPMarshalledQuestion *questions;
} MPMarshalledSite;
typedef struct MPMarshalledUser {
const char *fullName;
const char *masterPassword;
MPAlgorithmVersion algorithm;
bool redacted;
unsigned int avatar;
MPResultType defaultType;
time_t lastUsed;
size_t sites_count;
MPMarshalledSite *sites;
} MPMarshalledUser;
typedef struct MPMarshalInfo {
MPMarshalFormat format;
MPAlgorithmVersion algorithm;
const char *fullName;
const char *keyID;
bool redacted;
time_t date;
} MPMarshalInfo;
//// Marshalling.
/** Write the user and all associated data out to the given output buffer using the given marshalling format. */
bool mpw_marshal_write(
char **out, const MPMarshalFormat outFormat, const MPMarshalledUser *user, MPMarshalError *error);
/** Try to read metadata on the sites in the input buffer. */
MPMarshalInfo *mpw_marshal_read_info(
const char *in);
/** Unmarshall sites in the given input buffer by parsing it using the given marshalling format. */
MPMarshalledUser *mpw_marshal_read(
const char *in, const MPMarshalFormat inFormat, const char *masterPassword, MPMarshalError *error);
//// Utilities.
/** Create a new user object ready for marshalling. */
MPMarshalledUser *mpw_marshal_user(
const char *fullName, const char *masterPassword, const MPAlgorithmVersion algorithmVersion);
/** Create a new site attached to the given user object, ready for marshalling. */
MPMarshalledSite *mpw_marshal_site(
MPMarshalledUser *user,
const char *siteName, const MPResultType resultType, const MPCounterValue siteCounter, const MPAlgorithmVersion algorithmVersion);
/** Create a new question attached to the given site object, ready for marshalling. */
MPMarshalledQuestion *mpw_marshal_question(
MPMarshalledSite *site, const char *keyword);
/** Free the given user object and all associated data. */
bool mpw_marshal_info_free(
MPMarshalInfo **info);
bool mpw_marshal_free(
MPMarshalledUser **user);
//// Format.
/**
* @return The purpose represented by the given name.
*/
const MPMarshalFormat mpw_formatWithName(
const char *formatName);
/**
* @return The standard name for the given purpose.
*/
const char *mpw_nameForFormat(
const MPMarshalFormat format);
/**
* @return The file extension that's recommended for files that use the given marshalling format.
*/
const char *mpw_marshal_format_extension(
const MPMarshalFormat format);
#endif // _MPW_MARSHAL_H

View File

@@ -0,0 +1,267 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include <ctype.h>
#include "mpw-types.h"
#include "mpw-util.h"
const size_t MPMasterKeySize = 64;
const size_t MPSiteKeySize = 256 / 8; // Size of HMAC-SHA-256
const MPResultType mpw_typeWithName(const char *typeName) {
// Find what password type is represented by the type letter.
if (strlen( typeName ) == 1) {
if ('x' == typeName[0])
return MPResultTypeTemplateMaximum;
if ('l' == typeName[0])
return MPResultTypeTemplateLong;
if ('m' == typeName[0])
return MPResultTypeTemplateMedium;
if ('b' == typeName[0])
return MPResultTypeTemplateBasic;
if ('s' == typeName[0])
return MPResultTypeTemplateShort;
if ('i' == typeName[0])
return MPResultTypeTemplatePIN;
if ('n' == typeName[0])
return MPResultTypeTemplateName;
if ('p' == typeName[0])
return MPResultTypeTemplatePhrase;
if ('P' == typeName[0])
return MPResultTypeStatefulPersonal;
if ('D' == typeName[0])
return MPResultTypeStatefulDevice;
if ('K' == typeName[0])
return MPResultTypeDeriveKey;
}
// Lower-case typeName to standardize it.
size_t stdTypeNameSize = strlen( typeName );
char stdTypeName[stdTypeNameSize + 1];
for (size_t c = 0; c < stdTypeNameSize; ++c)
stdTypeName[c] = (char)tolower( typeName[c] );
stdTypeName[stdTypeNameSize] = '\0';
// Find what password type is represented by the type name.
if (strncmp( mpw_nameForType( MPResultTypeTemplateMaximum ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplateMaximum;
if (strncmp( mpw_nameForType( MPResultTypeTemplateLong ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplateLong;
if (strncmp( mpw_nameForType( MPResultTypeTemplateMedium ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplateMedium;
if (strncmp( mpw_nameForType( MPResultTypeTemplateBasic ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplateBasic;
if (strncmp( mpw_nameForType( MPResultTypeTemplateShort ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplateShort;
if (strncmp( mpw_nameForType( MPResultTypeTemplatePIN ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplatePIN;
if (strncmp( mpw_nameForType( MPResultTypeTemplateName ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplateName;
if (strncmp( mpw_nameForType( MPResultTypeTemplatePhrase ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeTemplatePhrase;
if (strncmp( mpw_nameForType( MPResultTypeStatefulPersonal ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeStatefulPersonal;
if (strncmp( mpw_nameForType( MPResultTypeStatefulDevice ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeStatefulDevice;
if (strncmp( mpw_nameForType( MPResultTypeDeriveKey ), stdTypeName, strlen( stdTypeName ) ) == 0)
return MPResultTypeDeriveKey;
dbg( "Not a generated type name: %s", stdTypeName );
return (MPResultType)ERR;
}
const char *mpw_nameForType(MPResultType resultType) {
switch (resultType) {
case MPResultTypeTemplateMaximum:
return "maximum";
case MPResultTypeTemplateLong:
return "long";
case MPResultTypeTemplateMedium:
return "medium";
case MPResultTypeTemplateBasic:
return "basic";
case MPResultTypeTemplateShort:
return "short";
case MPResultTypeTemplatePIN:
return "pin";
case MPResultTypeTemplateName:
return "name";
case MPResultTypeTemplatePhrase:
return "phrase";
case MPResultTypeStatefulPersonal:
return "personal";
case MPResultTypeStatefulDevice:
return "device";
case MPResultTypeDeriveKey:
return "key";
default: {
dbg( "Unknown password type: %d", resultType );
return NULL;
}
}
}
const char **mpw_templatesForType(MPResultType type, size_t *count) {
if (!(type & MPResultTypeClassTemplate)) {
dbg( "Not a generated type: %d", type );
return NULL;
}
switch (type) {
case MPResultTypeTemplateMaximum:
return mpw_alloc_array( count, const char *,
"anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno" );
case MPResultTypeTemplateLong:
return mpw_alloc_array( count, const char *,
"CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
"CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
"CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
"CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
"CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
"CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
"CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno" );
case MPResultTypeTemplateMedium:
return mpw_alloc_array( count, const char *,
"CvcnoCvc", "CvcCvcno" );
case MPResultTypeTemplateShort:
return mpw_alloc_array( count, const char *,
"Cvcn" );
case MPResultTypeTemplateBasic:
return mpw_alloc_array( count, const char *,
"aaanaaan", "aannaaan", "aaannaaa" );
case MPResultTypeTemplatePIN:
return mpw_alloc_array( count, const char *,
"nnnn" );
case MPResultTypeTemplateName:
return mpw_alloc_array( count, const char *,
"cvccvcvcv" );
case MPResultTypeTemplatePhrase:
return mpw_alloc_array( count, const char *,
"cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv" );
default: {
dbg( "Unknown generated type: %d", type );
return NULL;
}
}
}
const char *mpw_templateForType(MPResultType type, uint8_t templateIndex) {
size_t count = 0;
const char **templates = mpw_templatesForType( type, &count );
char const *template = templates && count? templates[templateIndex % count]: NULL;
free( templates );
return template;
}
const MPKeyPurpose mpw_purposeWithName(const char *purposeName) {
// Lower-case and trim optionally leading "generated" string from typeName to standardize it.
size_t stdPurposeNameSize = strlen( purposeName );
char stdPurposeName[stdPurposeNameSize + 1];
for (size_t c = 0; c < stdPurposeNameSize; ++c)
stdPurposeName[c] = (char)tolower( purposeName[c] );
stdPurposeName[stdPurposeNameSize] = '\0';
if (strncmp( mpw_nameForPurpose( MPKeyPurposeAuthentication ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
return MPKeyPurposeAuthentication;
if (strncmp( mpw_nameForPurpose( MPKeyPurposeIdentification ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
return MPKeyPurposeIdentification;
if (strncmp( mpw_nameForPurpose( MPKeyPurposeRecovery ), stdPurposeName, strlen( stdPurposeName ) ) == 0)
return MPKeyPurposeRecovery;
dbg( "Not a purpose name: %s", stdPurposeName );
return (MPKeyPurpose)ERR;
}
const char *mpw_nameForPurpose(MPKeyPurpose purpose) {
switch (purpose) {
case MPKeyPurposeAuthentication:
return "authentication";
case MPKeyPurposeIdentification:
return "identification";
case MPKeyPurposeRecovery:
return "recovery";
default: {
dbg( "Unknown purpose: %d", purpose );
return NULL;
}
}
}
const char *mpw_scopeForPurpose(MPKeyPurpose purpose) {
switch (purpose) {
case MPKeyPurposeAuthentication:
return "com.lyndir.masterpassword";
case MPKeyPurposeIdentification:
return "com.lyndir.masterpassword.login";
case MPKeyPurposeRecovery:
return "com.lyndir.masterpassword.answer";
default: {
dbg( "Unknown purpose: %d", purpose );
return NULL;
}
}
}
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: {
dbg( "Unknown character class: %c", characterClass );
return NULL;
}
}
}
const char mpw_characterFromClass(char characterClass, uint8_t seedByte) {
const char *classCharacters = mpw_charactersInClass( characterClass );
if (!classCharacters)
return '\0';
return classCharacters[seedByte % strlen( classCharacters )];
}

View File

@@ -0,0 +1,180 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#ifndef _MPW_TYPES_H
#define _MPW_TYPES_H
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef NS_ENUM
#define mpw_enum(_type, _name) NS_ENUM(_type, _name)
#else
#define mpw_enum(_type, _name) _type _name; enum
#endif
#ifndef __unused
#define __unused __attribute__((unused))
#endif
//// Types.
extern const size_t MPMasterKeySize, MPSiteKeySize; /* bytes */
typedef const uint8_t *MPMasterKey, *MPSiteKey;
typedef const char *MPKeyID;
typedef mpw_enum( uint8_t, MPKeyPurpose ) {
/** Generate a key for authentication. */
MPKeyPurposeAuthentication,
/** Generate a name for identification. */
MPKeyPurposeIdentification,
/** Generate a recovery token. */
MPKeyPurposeRecovery,
};
// bit 4 - 9
typedef mpw_enum( uint16_t, MPResultTypeClass ) {
/** Use the site key to generate a password from a template. */
MPResultTypeClassTemplate = 1 << 4,
/** Use the site key to encrypt and decrypt a stateful entity. */
MPResultTypeClassStateful = 1 << 5,
/** Use the site key to derive a site-specific object. */
MPResultTypeClassDerive = 1 << 6,
};
// bit 10 - 15
typedef mpw_enum( uint16_t, MPSiteFeature ) {
/** Export the key-protected content data. */
MPSiteFeatureExportContent = 1 << 10,
/** Never export content. */
MPSiteFeatureDevicePrivate = 1 << 11,
/** Don't use this as the primary authentication result type. */
MPSiteFeatureAlternative = 1 << 12,
};
// bit 0-3 | MPResultTypeClass | MPSiteFeature
typedef mpw_enum( uint32_t, MPResultType ) {
/** 16: pg^VMAUBk5x3p%HP%i4= */
MPResultTypeTemplateMaximum = 0x0 | MPResultTypeClassTemplate | 0x0,
/** 17: BiroYena8:Kixa */
MPResultTypeTemplateLong = 0x1 | MPResultTypeClassTemplate | 0x0,
/** 18: BirSuj0- */
MPResultTypeTemplateMedium = 0x2 | MPResultTypeClassTemplate | 0x0,
/** 19: Bir8 */
MPResultTypeTemplateShort = 0x3 | MPResultTypeClassTemplate | 0x0,
/** 20: pO98MoD0 */
MPResultTypeTemplateBasic = 0x4 | MPResultTypeClassTemplate | 0x0,
/** 21: 2798 */
MPResultTypeTemplatePIN = 0x5 | MPResultTypeClassTemplate | 0x0,
/** 30: birsujano */
MPResultTypeTemplateName = 0xE | MPResultTypeClassTemplate | 0x0,
/** 31: bir yennoquce fefi */
MPResultTypeTemplatePhrase = 0xF | MPResultTypeClassTemplate | 0x0,
/** 1056: Custom saved password. */
MPResultTypeStatefulPersonal = 0x0 | MPResultTypeClassStateful | MPSiteFeatureExportContent,
/** 2081: Custom saved password that should not be exported from the device. */
MPResultTypeStatefulDevice = 0x1 | MPResultTypeClassStateful | MPSiteFeatureDevicePrivate,
/** 4160: Derive a unique binary key. */
MPResultTypeDeriveKey = 0x0 | MPResultTypeClassDerive | MPSiteFeatureAlternative,
MPResultTypeDefault = MPResultTypeTemplateLong,
};
typedef mpw_enum ( uint32_t, MPCounterValue ) {
/** Use a time-based counter value, resulting in a TOTP generator. */
MPCounterValueTOTP = 0,
/** The initial value for a site's counter. */
MPCounterValueInitial = 1,
MPCounterValueDefault = MPCounterValueInitial,
MPCounterValueFirst = MPCounterValueTOTP,
MPCounterValueLast = UINT32_MAX,
};
/** These colours are compatible with the original ANSI SGR. */
typedef mpw_enum( uint8_t, MPIdenticonColor ) {
MPIdenticonColorRed = 1,
MPIdenticonColorGreen,
MPIdenticonColorYellow,
MPIdenticonColorBlue,
MPIdenticonColorMagenta,
MPIdenticonColorCyan,
MPIdenticonColorWhite,
MPIdenticonColorFirst = MPIdenticonColorRed,
MPIdenticonColorLast = MPIdenticonColorWhite,
};
typedef struct {
const char *leftArm;
const char *body;
const char *rightArm;
const char *accessory;
MPIdenticonColor color;
} MPIdenticon;
//// Type utilities.
/**
* @return The purpose represented by the given name.
*/
const MPKeyPurpose mpw_purposeWithName(const char *purposeName);
/**
* @return The standard name for the given purpose.
*/
const char *mpw_nameForPurpose(MPKeyPurpose purpose);
/**
* @return An internal string containing the scope identifier to apply when encoding for the given purpose.
*/
const char *mpw_scopeForPurpose(MPKeyPurpose purpose);
/**
* @return The password type represented by the given name.
*/
const MPResultType mpw_typeWithName(const char *typeName);
/**
* @return The standard name for the given password type.
*/
const char *mpw_nameForType(MPResultType resultType);
/**
* @return A newly allocated array of internal strings that express the templates to use for the given type.
* The amount of elements in the array is stored in count.
* If an unsupported type is given, count will be 0 and will return NULL.
* The array needs to be free'ed, the strings themselves must not be free'ed or modified.
*/
const char **mpw_templatesForType(MPResultType 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(MPResultType type, uint8_t templateIndex);
/**
* @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);
#endif // _MPW_TYPES_H

View File

@@ -0,0 +1,498 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#include <string.h>
#include <ctype.h>
#include <errno.h>
#if MPW_CPERCIVA
#include <scrypt/crypto_scrypt.h>
#include <scrypt/sha256.h>
#elif MPW_SODIUM
#include "sodium.h"
#endif
#define AES_ECB 0
#define AES_CBC 1
#include "aes.h"
#include "mpw-util.h"
#ifdef inf_level
int mpw_verbosity = inf_level;
#endif
void mpw_uint16(const uint16_t number, uint8_t buf[2]) {
buf[0] = (uint8_t)((number >> 8L) & UINT8_MAX);
buf[1] = (uint8_t)((number >> 0L) & UINT8_MAX);
}
void mpw_uint32(const uint32_t number, uint8_t buf[4]) {
buf[0] = (uint8_t)((number >> 24) & UINT8_MAX);
buf[1] = (uint8_t)((number >> 16) & UINT8_MAX);
buf[2] = (uint8_t)((number >> 8L) & UINT8_MAX);
buf[3] = (uint8_t)((number >> 0L) & UINT8_MAX);
}
void mpw_uint64(const uint64_t number, uint8_t buf[8]) {
buf[0] = (uint8_t)((number >> 56) & UINT8_MAX);
buf[1] = (uint8_t)((number >> 48) & UINT8_MAX);
buf[2] = (uint8_t)((number >> 40) & UINT8_MAX);
buf[3] = (uint8_t)((number >> 32) & UINT8_MAX);
buf[4] = (uint8_t)((number >> 24) & UINT8_MAX);
buf[5] = (uint8_t)((number >> 16) & UINT8_MAX);
buf[6] = (uint8_t)((number >> 8L) & UINT8_MAX);
buf[7] = (uint8_t)((number >> 0L) & UINT8_MAX);
}
bool mpw_push_buf(uint8_t **buffer, size_t *bufferSize, const void *pushBuffer, const size_t pushSize) {
if (!buffer || !bufferSize || !pushBuffer || !pushSize)
return false;
if (*bufferSize == (size_t)ERR)
// The buffer was marked as broken, it is missing a previous push. Abort to avoid corrupt content.
return false;
if (!mpw_realloc( buffer, bufferSize, pushSize )) {
// realloc failed, we can't push. Mark the buffer as broken.
mpw_free( buffer, *bufferSize );
*bufferSize = (size_t)ERR;
return false;
}
uint8_t *bufferOffset = *buffer + *bufferSize - pushSize;
memcpy( bufferOffset, pushBuffer, pushSize );
return true;
}
bool mpw_push_string(uint8_t **buffer, size_t *bufferSize, const char *pushString) {
return pushString && mpw_push_buf( buffer, bufferSize, pushString, strlen( pushString ) );
}
bool mpw_string_push(char **string, const char *pushString) {
if (!string || !pushString)
return false;
if (!*string)
*string = calloc( 1, sizeof( char ) );
size_t stringLength = strlen( *string );
return pushString && mpw_push_buf( (uint8_t **const)string, &stringLength, pushString, strlen( pushString ) + 1 );
}
bool mpw_string_pushf(char **string, const char *pushFormat, ...) {
va_list args;
va_start( args, pushFormat );
bool success = mpw_string_push( string, mpw_vstr( pushFormat, args ) );
va_end( args );
return success;
}
bool mpw_push_int(uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt) {
uint8_t pushBuf[4 /* 32 / 8 */];
mpw_uint32( pushInt, pushBuf );
return mpw_push_buf( buffer, bufferSize, &pushBuf, sizeof( pushBuf ) );
}
bool __mpw_realloc(const void **buffer, size_t *bufferSize, const size_t deltaSize) {
if (!buffer)
return false;
void *newBuffer = realloc( (void *)*buffer, (bufferSize? *bufferSize: 0) + deltaSize );
if (!newBuffer)
return false;
*buffer = newBuffer;
if (bufferSize)
*bufferSize += deltaSize;
return true;
}
void mpw_zero(void *buffer, size_t bufferSize) {
uint8_t *b = buffer;
for (; bufferSize > 0; --bufferSize)
*b++ = 0;
}
bool __mpw_free(void **buffer, const size_t bufferSize) {
if (!buffer || !*buffer)
return false;
mpw_zero( *buffer, bufferSize );
free( *buffer );
*buffer = NULL;
return true;
}
bool __mpw_free_string(char **string) {
return *string && __mpw_free( (void **)string, strlen( *string ) );
}
bool __mpw_free_strings(char **strings, ...) {
bool success = true;
va_list args;
va_start( args, strings );
success &= mpw_free_string( strings );
for (char **string; (string = va_arg( args, char ** ));)
success &= mpw_free_string( string );
va_end( args );
return success;
}
uint8_t const *mpw_kdf_scrypt(const size_t keySize, const uint8_t *secret, const size_t secretSize, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p) {
if (!secret || !salt || !secretSize || !saltSize)
return NULL;
uint8_t *key = malloc( keySize );
if (!key)
return NULL;
#if MPW_CPERCIVA
if (crypto_scrypt( (const uint8_t *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
mpw_free( &key, keySize );
return NULL;
}
#elif MPW_SODIUM
if (crypto_pwhash_scryptsalsa208sha256_ll( secret, secretSize, salt, saltSize, N, r, p, key, keySize ) != 0) {
mpw_free( &key, keySize );
return NULL;
}
#else
#error No crypto support for mpw_scrypt.
#endif
return key;
}
uint8_t const *mpw_kdf_blake2b(const size_t subkeySize, const uint8_t *key, const size_t keySize,
const uint8_t *context, const size_t contextSize, const uint64_t id, const char *personal) {
if (!key || !keySize || !subkeySize) {
errno = EINVAL;
return NULL;
}
uint8_t *subkey = malloc( subkeySize );
if (!subkey)
return NULL;
#if MPW_SODIUM
if (keySize < crypto_generichash_blake2b_KEYBYTES_MIN || keySize > crypto_generichash_blake2b_KEYBYTES_MAX ||
subkeySize < crypto_generichash_blake2b_KEYBYTES_MIN || subkeySize > crypto_generichash_blake2b_KEYBYTES_MAX ||
(personal && strlen( personal ) > crypto_generichash_blake2b_PERSONALBYTES)) {
errno = EINVAL;
free( subkey );
return NULL;
}
uint8_t saltBuf[crypto_generichash_blake2b_SALTBYTES];
mpw_zero( saltBuf, sizeof saltBuf );
if (id)
mpw_uint64( id, saltBuf );
uint8_t personalBuf[crypto_generichash_blake2b_PERSONALBYTES];
mpw_zero( personalBuf, sizeof personalBuf );
if (personal && strlen( personal ))
memcpy( personalBuf, personal, strlen( personal ) );
if (crypto_generichash_blake2b_salt_personal( subkey, subkeySize, context, contextSize, key, keySize, saltBuf, personalBuf ) != 0) {
mpw_free( &subkey, subkeySize );
return NULL;
}
#else
#error No crypto support for mpw_kdf_blake2b.
#endif
return subkey;
}
uint8_t const *mpw_hash_hmac_sha256(const uint8_t *key, const size_t keySize, const uint8_t *message, const size_t messageSize) {
if (!key || !keySize || !message || !messageSize)
return NULL;
#if MPW_CPERCIVA
uint8_t *const mac = malloc( 32 );
if (!mac)
return NULL;
HMAC_SHA256_Buf( key, keySize, message, messageSize, mac );
#elif MPW_SODIUM
uint8_t *const mac = malloc( crypto_auth_hmacsha256_BYTES );
if (!mac)
return NULL;
crypto_auth_hmacsha256_state state;
if (crypto_auth_hmacsha256_init( &state, key, keySize ) != 0 ||
crypto_auth_hmacsha256_update( &state, message, messageSize ) != 0 ||
crypto_auth_hmacsha256_final( &state, mac ) != 0) {
mpw_free( &mac, crypto_auth_hmacsha256_BYTES );
return NULL;
}
#else
#error No crypto support for mpw_hmac_sha256.
#endif
return mac;
}
// We do our best to not fail on odd buf's, eg. non-padded cipher texts.
static uint8_t const *mpw_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, size_t *bufSize) {
if (!key || keySize < AES_BLOCKLEN || !*bufSize)
return NULL;
// IV = zero
uint8_t iv[AES_BLOCKLEN];
mpw_zero( iv, sizeof iv );
// Add PKCS#7 padding
uint32_t aesSize = ((uint32_t)*bufSize + AES_BLOCKLEN - 1) & -AES_BLOCKLEN; // round up to block size.
if (encrypt && !(*bufSize % AES_BLOCKLEN)) // add pad block if plain text fits block size.
encrypt += AES_BLOCKLEN;
uint8_t aesBuf[aesSize];
memcpy( aesBuf, buf, *bufSize );
memset( aesBuf + *bufSize, aesSize - *bufSize, aesSize - *bufSize );
uint8_t *resultBuf = malloc( aesSize );
if (encrypt)
AES_CBC_encrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
else
AES_CBC_decrypt_buffer( resultBuf, aesBuf, aesSize, key, iv );
mpw_zero( aesBuf, aesSize );
mpw_zero( iv, AES_BLOCKLEN );
// Truncate PKCS#7 padding
if (encrypt)
*bufSize = aesSize;
else if (*bufSize % AES_BLOCKLEN == 0 && resultBuf[aesSize - 1] < AES_BLOCKLEN)
*bufSize -= resultBuf[aesSize - 1];
return resultBuf;
}
uint8_t const *mpw_aes_encrypt(const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, size_t *bufSize) {
return mpw_aes( true, key, keySize, plainBuf, bufSize );
}
uint8_t const *mpw_aes_decrypt(const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, size_t *bufSize) {
return mpw_aes( false, key, keySize, cipherBuf, bufSize );
}
#if UNUSED
const char *mpw_hotp(const uint8_t *key, size_t keySize, uint64_t movingFactor, uint8_t digits, uint8_t truncationOffset) {
// Hash the moving factor with the key.
uint8_t counter[8];
mpw_uint64( movingFactor, counter );
uint8_t hash[20];
hmac_sha1( key, keySize, counter, sizeof( counter ), hash );
// Determine the offset to select OTP bytes from.
int offset;
if ((truncationOffset >= 0) && (truncationOffset < (sizeof( hash ) - 4)))
offset = truncationOffset;
else
offset = hash[sizeof( hash ) - 1] & 0xf;
// Select four bytes from the truncation offset.
uint32_t otp = 0U
| ((hash[offset + 0] & 0x7f) << 24)
| ((hash[offset + 1] & 0xff) << 16)
| ((hash[offset + 2] & 0xff) << 8)
| ((hash[offset + 3] & 0xff) << 0);
// Render the OTP as `digits` decimal digits.
otp %= (int)pow(10, digits);
return mpw_strdup( mpw_str( "%0*d", digits, otp ) );
}
#endif
MPKeyID mpw_id_buf(const void *buf, size_t length) {
if (!buf)
return "<unset>";
#if MPW_CPERCIVA
uint8_t hash[32];
SHA256_Buf( buf, length, hash );
#elif MPW_SODIUM
uint8_t hash[crypto_hash_sha256_BYTES];
crypto_hash_sha256( hash, buf, length );
#else
#error No crypto support for mpw_id_buf.
#endif
return mpw_hex( hash, sizeof( hash ) / sizeof( uint8_t ) );
}
bool mpw_id_buf_equals(const char *id1, const char *id2) {
size_t size = strlen( id1 );
if (size != strlen( id2 ))
return false;
for (size_t c = 0; c < size; ++c)
if (tolower( id1[c] ) != tolower( id2[c] ))
return false;
return true;
}
const char *mpw_str(const char *format, ...) {
va_list args;
va_start( args, format );
const char *str_str = mpw_vstr( format, args );
va_end( args );
return str_str;
}
const char *mpw_vstr(const char *format, va_list args) {
// TODO: We should find a way to get rid of this shared storage medium.
// TODO: Not thread-safe
static char *str_str;
static size_t str_str_max;
if (!str_str && !(str_str = calloc( str_str_max = 1, sizeof( char ) )))
return NULL;
do {
va_list args_attempt;
va_copy( args_attempt, args );
size_t len = (size_t)vsnprintf( str_str, str_str_max, format, args_attempt );
va_end( args_attempt );
if ((int)len < 0)
return NULL;
if (len < str_str_max)
break;
if (!mpw_realloc( &str_str, &str_str_max, len - str_str_max + 1 ))
return NULL;
} while (true);
return str_str;
}
const char *mpw_hex(const void *buf, size_t length) {
// TODO: We should find a way to get rid of this shared storage medium.
// TODO: Not thread-safe
static char **mpw_hex_buf;
static unsigned int mpw_hex_buf_i;
if (!mpw_hex_buf)
mpw_hex_buf = calloc( 10, sizeof( char * ) );
mpw_hex_buf_i = (mpw_hex_buf_i + 1) % 10;
if (mpw_realloc( &mpw_hex_buf[mpw_hex_buf_i], NULL, length * 2 + 1 ))
for (size_t kH = 0; kH < length; kH++)
sprintf( &(mpw_hex_buf[mpw_hex_buf_i][kH * 2]), "%02X", ((const uint8_t *)buf)[kH] );
return mpw_hex_buf[mpw_hex_buf_i];
}
const char *mpw_hex_l(uint32_t number) {
uint8_t buf[4 /* 32 / 8 */];
buf[0] = (uint8_t)((number >> 24) & UINT8_MAX);
buf[1] = (uint8_t)((number >> 16) & UINT8_MAX);
buf[2] = (uint8_t)((number >> 8L) & UINT8_MAX);
buf[3] = (uint8_t)((number >> 0L) & UINT8_MAX);
return mpw_hex( &buf, sizeof( buf ) );
}
/**
* @return the amount of bytes used by UTF-8 to encode a single character that starts with the given byte.
*/
static int mpw_utf8_sizeof(unsigned char utf8Byte) {
if (!utf8Byte)
return 0;
if ((utf8Byte & 0x80) == 0)
return 1;
if ((utf8Byte & 0xC0) != 0xC0)
return 0;
if ((utf8Byte & 0xE0) == 0xC0)
return 2;
if ((utf8Byte & 0xF0) == 0xE0)
return 3;
if ((utf8Byte & 0xF8) == 0xF0)
return 4;
return 0;
}
const size_t mpw_utf8_strlen(const char *utf8String) {
size_t charlen = 0;
char *remainingString = (char *)utf8String;
for (int charByteSize; (charByteSize = mpw_utf8_sizeof( (unsigned char)*remainingString )); remainingString += charByteSize)
++charlen;
return charlen;
}
char *mpw_strdup(const char *src) {
if (!src)
return NULL;
size_t len = strlen( src );
char *dst = malloc( len + 1 );
memcpy( dst, src, len );
dst[len] = '\0';
return dst;
}
char *mpw_strndup(const char *src, size_t max) {
if (!src)
return NULL;
size_t len = 0;
for (; len < max && src[len] != '\0'; ++len);
char *dst = malloc( len + 1 );
memcpy( dst, src, len );
dst[len] = '\0';
return dst;
}

View File

@@ -0,0 +1,211 @@
//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================
#ifndef _MPW_UTIL_H
#define _MPW_UTIL_H
#include <stdio.h>
#include <stdarg.h>
#include "mpw-types.h"
//// Logging.
extern int mpw_verbosity;
#ifndef mpw_log_do
#define mpw_log_do(level, format, ...) \
fprintf( stderr, format "\n", ##__VA_ARGS__ )
#endif
#ifndef mpw_log
#define mpw_log(level, ...) ({ \
if (mpw_verbosity >= level) { \
mpw_log_do( level, ##__VA_ARGS__ ); \
}; })
#endif
#ifndef trc
/** Logging internal state. */
#define trc_level 3
#define trc(...) mpw_log( trc_level, ##__VA_ARGS__ )
/** Logging state and events interesting when investigating issues. */
#define dbg_level 2
#define dbg(...) mpw_log( dbg_level, ##__VA_ARGS__ )
/** User messages. */
#define inf_level 1
#define inf(...) mpw_log( inf_level, ##__VA_ARGS__ )
/** Recoverable issues and user suggestions. */
#define wrn_level 0
#define wrn(...) mpw_log( wrn_level, ##__VA_ARGS__ )
/** Unrecoverable issues. */
#define err_level -1
#define err(...) mpw_log( err_level, ##__VA_ARGS__ )
/** Issues that lead to abortion. */
#define ftl_level -2
#define ftl(...) mpw_log( ftl_level, ##__VA_ARGS__ )
#endif
#ifndef min
#define min(a, b) ({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; })
#endif
#ifndef max
#define max(a, b) ({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
#endif
#ifndef ERR
#define ERR -1
#endif
#ifndef OK
#define OK 0
#endif
#ifndef stringify
#define stringify(s) #s
#endif
#ifndef stringify_def
#define stringify_def(s) stringify(s)
#endif
//// Buffers and memory.
/** Write a number to a byte buffer using mpw's endianness (big/network endian). */
void mpw_uint16(const uint16_t number, uint8_t buf[2]);
void mpw_uint32(const uint32_t number, uint8_t buf[4]);
void mpw_uint64(const uint64_t number, uint8_t buf[8]);
/** Allocate a new array of _type, assign its element count to _count if not NULL and populate it with the varargs. */
#define mpw_alloc_array(_count, _type, ...) ({ \
_type stackElements[] = { __VA_ARGS__ }; \
if (_count) \
*_count = sizeof( stackElements ) / sizeof( _type ); \
_type *allocElements = malloc( sizeof( stackElements ) ); \
memcpy( allocElements, stackElements, sizeof( stackElements ) ); \
allocElements; \
})
/** Push a buffer onto a buffer. reallocs the given buffer and appends the given buffer. */
bool mpw_push_buf(
uint8_t **buffer, size_t *bufferSize, const void *pushBuffer, const size_t pushSize);
/** Push a string onto a buffer. reallocs the given buffer and appends the given string. */
bool mpw_push_string(
uint8_t **buffer, size_t *bufferSize, const char *pushString);
/** Push a string onto another string. reallocs the target string and appends the source string. */
bool mpw_string_push(
char **string, const char *pushString);
bool mpw_string_pushf(
char **string, const char *pushFormat, ...);
/** Push an integer onto a buffer. reallocs the given buffer and appends the given integer. */
bool mpw_push_int(
uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt);
/** Reallocate the given buffer from the given size by adding the delta size.
* On success, the buffer size pointer will be updated to the buffer's new size
* and the buffer pointer may be updated to a new memory address.
* On failure, the buffer and pointers will remain unaffected.
* @param buffer A pointer to the buffer to reallocate.
* @param bufferSize A pointer to the buffer's actual size.
* @param deltaSize The amount to increase the buffer's size by.
* @return true if successful, false if reallocation failed.
*/
#define mpw_realloc(buffer, bufferSize, deltaSize) \
({ __typeof__(buffer) _b = buffer; const void *__b = *_b; (void)__b; __mpw_realloc( (const void **)_b, bufferSize, deltaSize ); })
bool __mpw_realloc(const void **buffer, size_t *bufferSize, const size_t deltaSize);
void mpw_zero(
void *buffer, size_t bufferSize);
/** Free a buffer after zero'ing its contents, then set the reference to NULL. */
#define mpw_free(buffer, bufferSize) \
({ __typeof__(buffer) _b = buffer; const void *__b = *_b; (void)__b; __mpw_free( (void **)_b, bufferSize ); })
bool __mpw_free(
void **buffer, size_t bufferSize);
/** Free a string after zero'ing its contents, then set the reference to NULL. */
#define mpw_free_string(string) \
({ __typeof__(string) _s = string; const char *__s = *_s; (void)__s; __mpw_free_string( (char **)_s ); })
bool __mpw_free_string(
char **string);
/** Free strings after zero'ing their contents, then set the references to NULL. Terminate the va_list with NULL. */
#define mpw_free_strings(strings, ...) \
({ __typeof__(strings) _s = strings; const char *__s = *_s; (void)__s; __mpw_free_strings( (char **)_s, __VA_ARGS__ ); })
bool __mpw_free_strings(
char **strings, ...);
//// Cryptographic functions.
/** Derive a key from the given secret and salt using the scrypt KDF.
* @return A new keySize allocated buffer containing the key. */
uint8_t const *mpw_kdf_scrypt(
const size_t keySize, const uint8_t *secret, const size_t secretSize, const uint8_t *salt, const size_t saltSize,
uint64_t N, uint32_t r, uint32_t p);
/** Derive a subkey from the given key using the blake2b KDF.
* @return A new keySize allocated buffer containing the key. */
uint8_t const *mpw_kdf_blake2b(
const size_t subkeySize, const uint8_t *key, const size_t keySize,
const uint8_t *context, const size_t contextSize, const uint64_t id, const char *personal);
/** Calculate the MAC for the given message with the given key using SHA256-HMAC.
* @return A new 32-byte allocated buffer containing the MAC. */
uint8_t const *mpw_hash_hmac_sha256(
const uint8_t *key, const size_t keySize, const uint8_t *salt, const size_t saltSize);
/** Encrypt a plainBuf with the given key using AES-128-CBC.
* @return A new bufSize allocated buffer containing the cipherBuf. */
uint8_t const *mpw_aes_encrypt(
const uint8_t *key, const size_t keySize, const uint8_t *plainBuf, size_t *bufSize);
/** Decrypt a cipherBuf with the given key using AES-128-CBC.
* @return A new bufSize allocated buffer containing the plainBuf. */
uint8_t const *mpw_aes_decrypt(
const uint8_t *key, const size_t keySize, const uint8_t *cipherBuf, size_t *bufSize);
/** Calculate an OTP using RFC-4226.
* @return A newly allocated string containing exactly `digits` decimal OTP digits. */
#if UNUSED
const char *mpw_hotp(
const uint8_t *key, size_t keySize, uint64_t movingFactor, uint8_t digits, uint8_t truncationOffset);
#endif
//// Visualizers.
/** Compose a formatted string.
* @return A C-string in a reused buffer, do not free or store it. */
const char *mpw_str(const char *format, ...);
const char *mpw_vstr(const char *format, va_list args);
/** 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);
const char *mpw_hex_l(uint32_t number);
/** Encode a fingerprint for a buffer.
* @return A C-string in a reused buffer, do not free or store it. */
MPKeyID mpw_id_buf(const void *buf, size_t length);
/** Compare two fingerprints for equality.
* @return true if the buffers represent identical fingerprints. */
bool mpw_id_buf_equals(const char *id1, const char *id2);
//// String utilities.
/** @return The amount of display characters in the given UTF-8 string. */
const size_t mpw_utf8_strlen(const char *utf8String);
/** Drop-in for POSIX strdup(3). */
char *mpw_strdup(const char *src);
/** Drop-in for POSIX strndup(3). */
char *mpw_strndup(const char *src, size_t max);
#endif // _MPW_UTIL_H