Merge pull request #23 from rfjakob/master

Add integration tests & Compare MACs in constant time
This commit is contained in:
Valient Gough 2014-10-17 13:58:42 -07:00
commit 744f56b95d
6 changed files with 262 additions and 4 deletions

View File

@ -206,12 +206,18 @@ ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
uint64_t mac = cipher->MAC_64( tmp.data + macBytes, uint64_t mac = cipher->MAC_64( tmp.data + macBytes,
readSize - macBytes, key ); readSize - macBytes, key );
// Constant time comparision to prevent timing attacks
unsigned char fail = 0;
for(int i=0; i<macBytes; ++i, mac >>= 8) for(int i=0; i<macBytes; ++i, mac >>= 8)
{ {
int test = mac & 0xff; int test = mac & 0xff;
int stored = tmp.data[i]; int stored = tmp.data[i];
if(test != stored)
{ fail |= (test ^ stored);
}
if( fail > 0 )
{
// uh oh.. // uh oh..
long blockNum = req.offset / bs; long blockNum = req.offset / bs;
rWarning(_("MAC comparison failure in block %li"), rWarning(_("MAC comparison failure in block %li"),
@ -222,8 +228,6 @@ ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
throw ERROR( throw ERROR(
_("MAC comparison failure, refusing to read")); _("MAC comparison failure, refusing to read"));
} }
break;
}
} }
} }

37
tests/default.xml Normal file
View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="10">
<cfg class_id="0" tracking_level="0" version="20">
<version>20100713</version>
<creator>EncFS 1.7.5</creator>
<cipherAlg class_id="1" tracking_level="0" version="0">
<name>ssl/aes</name>
<major>3</major>
<minor>0</minor>
</cipherAlg>
<nameAlg>
<name>nameio/block</name>
<major>4</major>
<minor>0</minor>
</nameAlg>
<keySize>192</keySize>
<blockSize>1024</blockSize>
<uniqueIV>1</uniqueIV>
<chainedNameIV>1</chainedNameIV>
<externalIVChaining>0</externalIVChaining>
<blockMACBytes>0</blockMACBytes>
<blockMACRandBytes>0</blockMACRandBytes>
<allowHoles>1</allowHoles>
<encodedKeySize>44</encodedKeySize>
<encodedKeyData>
AL2iRMLYyuf7RXcFCK0CcegQ/+enbaaqTq1OY3WULMLpcz0yvo8K4KtVw7c=
</encodedKeyData>
<saltLen>20</saltLen>
<saltData>
RVCTqJyxxp7ZN0AsLJhgpvSuUB8=
</saltData>
<kdfIterations>170562</kdfIterations>
<desiredKDFDuration>500</desiredKDFDuration>
</cfg>
</boost_serialization>

37
tests/mac.xml Normal file
View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="10">
<cfg class_id="0" tracking_level="0" version="20">
<version>20100713</version>
<creator>EncFS 1.7.5</creator>
<cipherAlg class_id="1" tracking_level="0" version="0">
<name>ssl/aes</name>
<major>3</major>
<minor>0</minor>
</cipherAlg>
<nameAlg>
<name>nameio/block</name>
<major>4</major>
<minor>0</minor>
</nameAlg>
<keySize>192</keySize>
<blockSize>1024</blockSize>
<uniqueIV>1</uniqueIV>
<chainedNameIV>1</chainedNameIV>
<externalIVChaining>0</externalIVChaining>
<blockMACBytes>8</blockMACBytes>
<blockMACRandBytes>0</blockMACRandBytes>
<allowHoles>1</allowHoles>
<encodedKeySize>44</encodedKeySize>
<encodedKeyData>
AL2iRMLYyuf7RXcFCK0CcegQ/+enbaaqTq1OY3WULMLpcz0yvo8K4KtVw7c=
</encodedKeyData>
<saltLen>20</saltLen>
<saltData>
RVCTqJyxxp7ZN0AsLJhgpvSuUB8=
</saltData>
<kdfIterations>170562</kdfIterations>
<desiredKDFDuration>500</desiredKDFDuration>
</cfg>
</boost_serialization>

109
tests/run.sh Executable file
View File

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

31
tests/test_corruption.sh Normal file
View File

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

View File

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