diff --git a/encfs/MACFileIO.cpp b/encfs/MACFileIO.cpp index 1c30f13..b5ac807 100644 --- a/encfs/MACFileIO.cpp +++ b/encfs/MACFileIO.cpp @@ -206,12 +206,18 @@ ssize_t MACFileIO::readOneBlock( const IORequest &req ) const uint64_t mac = cipher->MAC_64( tmp.data + macBytes, readSize - macBytes, key ); + // Constant time comparision to prevent timing attacks + unsigned char fail = 0; for(int i=0; i>= 8) { int test = mac & 0xff; int stored = tmp.data[i]; - if(test != stored) - { + + fail |= (test ^ stored); + } + + if( fail > 0 ) + { // uh oh.. long blockNum = req.offset / bs; rWarning(_("MAC comparison failure in block %li"), @@ -222,8 +228,6 @@ ssize_t MACFileIO::readOneBlock( const IORequest &req ) const throw ERROR( _("MAC comparison failure, refusing to read")); } - break; - } } } diff --git a/tests/default.xml b/tests/default.xml new file mode 100644 index 0000000..ef399f5 --- /dev/null +++ b/tests/default.xml @@ -0,0 +1,37 @@ + + + + + 20100713 + EncFS 1.7.5 + + ssl/aes + 3 + 0 + + + nameio/block + 4 + 0 + + 192 + 1024 + 1 + 1 + 0 + 0 + 0 + 1 + 44 + +AL2iRMLYyuf7RXcFCK0CcegQ/+enbaaqTq1OY3WULMLpcz0yvo8K4KtVw7c= + + 20 + +RVCTqJyxxp7ZN0AsLJhgpvSuUB8= + + 170562 + 500 + + + diff --git a/tests/mac.xml b/tests/mac.xml new file mode 100644 index 0000000..920d4e9 --- /dev/null +++ b/tests/mac.xml @@ -0,0 +1,37 @@ + + + + + 20100713 + EncFS 1.7.5 + + ssl/aes + 3 + 0 + + + nameio/block + 4 + 0 + + 192 + 1024 + 1 + 1 + 0 + 8 + 0 + 1 + 44 + +AL2iRMLYyuf7RXcFCK0CcegQ/+enbaaqTq1OY3WULMLpcz0yvo8K4KtVw7c= + + 20 + +RVCTqJyxxp7ZN0AsLJhgpvSuUB8= + + 170562 + 500 + + + diff --git a/tests/run.sh b/tests/run.sh new file mode 100755 index 0000000..4af8907 --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +# This is the wrapper script for all tests. +# It calls the unit tests and all integration tests. +# +# Functions that are useful in more than one integration test +# should be defined here. + +set -eu + +# Set up envrionment variables, cd to the correct directory +function init { + + # Make sure we are in the "tests" directory + TESTDIR=$(dirname $(realpath "$0")) + cd $TESTDIR + + # Full path to encfs binary + ENCFS=$(realpath ../encfs/encfs) + + # Directory for temporary files (scratch) + SCRATCH=$(realpath scratch) + LOWER=$SCRATCH/lower + UPPER=$SCRATCH/upper + + # Test number counter + TESTNO=1 + + trap 'echo -e "***\e[31m test(s) FAILED\e[0m"' ERR +} + +# Run all test_*.sh files +function run_cli_tests { + + cd $TESTDIR + for i in $(echo test_*.sh) + do + cleanup + mkdir $SCRATCH + echo "*** running $i" + source $i + done +} + +# fresh_mount CONFIG +# +# Mount a fresh, empty encfs filesystem using the encfs config file +# CONFIG as .encfs6.xml. The password must be set to "a". +# The backing files will be at $LOWER, the mounted filesystem at $UPPER. +function fresh_mount { + cd $TESTDIR + fusermount -q -u $UPPER 2> /dev/null || true + wait + rm -Rf $LOWER $UPPER + mkdir -p $LOWER $UPPER + touch $UPPER/not_yet_mounted + + cp $TESTDIR/$1 $SCRATCH/lower/.encfs6.xml + + echo a | $ENCFS -f -S -o nonempty $LOWER $UPPER 2> /dev/null & + while [ -e $UPPER/not_yet_mounted ] + do + sleep 0.1s + done +} + +# Clean up scratch directory +function cleanup { + test -d $SCRATCH || return 0 + + cd $SCRATCH/.. + fusermount -q -u $UPPER 2> /dev/null || true + wait + rm -Rf $SCRATCH +} + +# Get the plain MD5 sum of a file, without the filename that is output +# by md5sum +# md5sum foo: 5f47bbbd6db883f93f5d00fd05f149ff foo +# plain_md5 foo: 5f47bbbd6db883f93f5d00fd05f149ff +function md5 { + OUTPUT=$(md5sum "$1") # Capture md5sum output + ARRAY=($OUTPUT) # Split into array + echo ${ARRAY[0]} # Return first element +} + +# Indicate beginning of a test +# Prints test number and title +function test_begin { + echo -n "$TESTNO $1: " + let TESTNO++ +} + +# Indicate successful completion of a test +function test_ok { + echo "OK" +} + +init + +test_begin "Running unit tests" +../encfs/test 2> /dev/null +test_ok + +run_cli_tests + +echo -e "***\e[32m All tests OK\e[0m" + +cleanup diff --git a/tests/test_corruption.sh b/tests/test_corruption.sh new file mode 100644 index 0000000..861a675 --- /dev/null +++ b/tests/test_corruption.sh @@ -0,0 +1,31 @@ +fresh_mount default.xml +cd $SCRATCH +dd if=/dev/urandom of=foo bs=1M count=1 2> /dev/null +A=$(md5 foo) +cp foo $UPPER +cd $UPPER +test_begin "Reading file" +B=$(md5 foo) +test $A = $B +test_ok + +test_begin "Reading corrupted file" +echo DEADBEEF >> $LOWER/$(ls $LOWER) +B=$(md5 foo) +test $A != $B +test_ok + +fresh_mount mac.xml +cd $SCRATCH +cp foo $UPPER +cd $UPPER +test_begin "Reading file with MAC" +B=$(md5 foo) +test $A = $B +test_ok + +test_begin "Corruption with MAC returns IO error" +echo DEADBEEF >> $LOWER/$(ls $LOWER) +md5 foo 2>&1 | grep "Input/output error" > /dev/null +test_ok + diff --git a/tests/test_file_expansion.sh b/tests/test_file_expansion.sh new file mode 100644 index 0000000..8abaaea --- /dev/null +++ b/tests/test_file_expansion.sh @@ -0,0 +1,40 @@ +fresh_mount default.xml +cd $UPPER +test_begin "Creating files of different sizes: " +for i in `seq 0 50` `seq 1000 1050` +do + OUTPUT=$(dd if=/dev/zero bs=$i count=1 2> /dev/null | tee $i | md5sum) + ARRAY=($OUTPUT) + A=${ARRAY[0]} # Remove filename + B=$(md5 $i) + test $A = $B +done +test_ok + +test_begin "Growing file" +rm -f ../grow +for i in `seq 0 300` +do + echo -n "abcdefg" >> ../grow + echo -n "abcdefg" >> grow + + A=$(md5 ../grow) + B=$(md5 grow) + test "$A" = "$B" +done +test_ok + +test_begin "Internal modification" +dd if=/dev/urandom of=../internal bs=1M count=2 2> /dev/null +cp ../internal internal +for i in 0 30 1020 1200 +do + dd if=/dev/zero of=../internal bs=1 count=1 skip=$i 2> /dev/null + dd if=/dev/zero of=internal bs=1 count=1 skip=$i 2> /dev/null + + A=$(md5 ../internal) + B=$(md5 internal) + test "$A" = "$B" +done +test_ok +