В данной статье я попытаюсь сравнить производительность различных систем шифрования под linux. В теории, конечно, известно, какая система производительнее, и попытки посчитать производительность разных систем были (например). Truecrypt даже содержит встроенный бенчмарк (который показывает, однако, производительность на RAM, его можно использовать разве что для оценки скорости разных алгоритмов шифрования). Я же сделаю несколько другое — измерю скорость файловой системы, зашифрованной разными средствами, в процентном соотношении по сравнению с обычной нешифрованной файловой системой.
Шифровать будем отдельный раздел на отдельном HDD, не содержащий корневую файловую систему, алгоритмом, использующимся по-умолчанию в каждом конкретном случае. Как обычный пользователь, я не разбираюсь в нюансах стандартов шифрования (например, чем отличается хэширование RIPEMD-160 от Whirpool, какой из этих режимов быстрее, какой способствует более высокой защите), поэтому просто положимся на то, что производители каждого программного продукта выбрали достаточно криптостойкие параметры по-умолчанию. Может, это и не совсем корректно, т. к. производительность различных алгоритмов шифрования неодинакова. При желании, конечно можно сменить тип шифрования, но я не уверен, что во всех тестируемых продуктах существует абсолютно идентичный набор алгоритмов. Тестировать будем:
1) LUKS — нативная система шифрования, в теории должна быть самой быстрой. Тома, зашифрованные LUKS можно использовать в Windows через FreeOTFE.
2) Truecrypt — в отличии от LUKS имеет, кроме консольного, и GUI-интерфейс, что делает его на порядок удобнее в использовании. Мультиплатформенное ПО. Обеспечение двух уровней правдоподобного отрицания наличия зашифрованных данных, необходимого в случае вынужденного открытия пароля пользователем. Тома Truecrypt не имеют заголовка, их нельзя отличить от набора случайных данных. В теории медленнее, чем LUKS, т.к. использует FUSE.
3) eCryptfs — система, по умолчанию предлагаемая пользователям Ubuntu для шифрования домашних каталогов, поэтому и включена в данный тест. Работает поверх уже существующей ФС. Шифрует каждый файл отдельно, поэтому всем видны права, даты изменения, количество зашифрованных файлов; по-умолчанию также видны имена файлов, хотя и существует опция для их шифрования. Самое малофункциональное средство из представленных.
Итак, для тестов выделена отдельная машина довольно преклонного возраста в следующей конфигурации: ЦП — Intel Celeron 2000Mhz, ОЗУ — 512 Mb DDR PC2700, системный HDD — WD Caviar SE 5400 RPM 80Gb, тестовый HDD — WD Caviar SE 7200 RPM 80Gb.
ОС — Ubuntu 12.04 LTS, версии всего ПО актуальные для репозиториев этой ОС на момент написания статьи (Truecrypt 7.1a-linux-x86 не из репозиториев).
Тестировать будем дефолтную для большинства дистрибутивов файловую систему ext4. Для тестирования производительности будем использовать утилиту iozone3 и написанный «на коленке» shell-скрипт для измерения процентной разницы в тестах.
#!/bin/sh
gendifffile () {
#процедура генерирует файл, который удобно анализировать. Во-первых, обрезаются
#не подлежащие анализу строки; во-вторых, в каждой строке обрезаются первых два числа, обозначающие
#размер файла и размер записи соответственно; в-третьих, весь файл выводится построчно -
#один результат теста на одну строку
cat $1 | while read LINE ; do
echo $LINE| grep "^[[:space:]]*[[:digit:]]" | awk '{for (i=3;i<=NF;i++) {print $i}}'
done >> $2
}
getline () {
#процедура выводит строку номер $2 файла $1
head -n $2 "$1" | tail -n 1
}
compare () {
#процедура сравнивает построчно файлы $1 и $2, вычисляя процентную разницу каждой пары тестов
#затем вычисляется среднее арифметическое значение, на сколько процентов быстрее или медленнее
#файл, содержащий первую группу тестов, файла, содержащего вторую группу
P=0
MAX=0
L1=`cat "$1" | wc -l` #количество тестов в файле
L2=`cat "$2" | wc -l`
if [ $L1 -ne $L2 ]; then #если файлы содержат разное количество тестов, то сравнивать их мы не будем
echo error
return
fi
STEP=$(( $L1*5/100 ))
J=0
for I in `seq 1 $L1`; do
J=$(( $J+1 ))
if [ $J -eq $STEP ]; then
J=0
echo "$(( 100*$I/$L1 ))% завершено ($I из $L1)"
fi
A=`getline "$1" $I`
B=`getline "$2" $I`
if [ `echo $A > $B|bc -l` -eq 1 ]; then
D=`echo "100-($B*100/$A)"|bc -l`
if [ `echo $D > $MAX| bc -l` -eq "1" ]; then
MAX=$D
sleep 5
fi
else
D=`echo "100-($A*100/$B)"|bc -l`
if [ `echo $D > $MAX| bc -l` -eq "1" ]; then
MAX=$D
sleep 5
fi
D="-$D" #если значение имеет знак "-", значит, данный тест был выполнен быстрее
#во втором файле, а не в первом
fi
P=`echo "$P+$D"| bc -l`
done
P=`echo $P/$L1| bc -l` #вычислим среднее арифметическое
echo PERCENT=$P MAX_PERCENT=$MAX
}
genaverage () {
#процедура генерации подготовленного к анализу файла, каждой строкой которого является
#среднее арифметическое соответствующих строк всех файлов отчётов, лежащих в анализируемой директории
AVG=`mktemp`
F=`ls "$1"|wc -l` #количество файлов с отчётами в заданной директории
#при условии, что там хранятся только такие файлы и больше ничего другого
#проверять корректность данного допущения мы не будем
if [ ! -d "$1" -o $F -lt 2 ]; then
echo error >/dev/stderr #в этой процедуре будем выводить все сообщения в stderr, т.к.
#stdout подставляется в другую процедуру
rm -f $AVG
exit
fi
TMP=`mktemp`
find "$1" -type f| while read FILE; do #для каждого файла отчёта iozone, лежащего в заданной директории
I=`mktemp` #сгенерируем временный файл, подготовленный для анализа
gendifffile "$FILE" "$I" #имена всех таких файлов запишем в "TMP" построчно
echo "$I">>$TMP
done
L=`cat `getline "$TMP" 1`|wc -l`
cat "$TMP"| while read LINE; do #немного проверок не помешает
L1=`cat "$LINE"| wc -l` #все ли файлы содержат одинаковое количество тестов
if [ $L -ne $L1 ]; then
echo error >/dev/stderr
exit
fi
done
STEP=$(( $L*5/100 ))
J=0
for I in `seq 1 $L`; do
J=$(( $J+1 ))
if [ $J -eq $STEP ]; then
J=0
echo "$(( 100*$I/$L ))% завершено ($I из $L)" >/dev/stderr
fi
SUMFILE=`mktemp` #таким образом я получаю значение переменной SUM из вложенного цикла
SUM=0
cat "$TMP"| while read LINE; do
SUM=$(( `getline "$LINE" $I`+$SUM ))
echo $SUM > "$SUMFILE"
done
echo `tail -n 1 "$SUMFILE"`/$F|bc -l >> $AVG #получаем среднее арифметическое
#и запишем его в соответствующее место
#файла AVG
rm -f "$SUMFILE"
done
cat "$TMP"| while read LINE; do #удалим временныe файлы
rm -f "$LINE"
done
rm -f "$TMP"
echo $AVG
}
printf %b "33[1;31mШаг 1/3n"
printf %b "33[1;37m"
echo Генерируем усреднённый файл для директории ""$1""
A=`genaverage "$1"`
printf %b "33[1;31mШаг 2/3n"
printf %b "33[1;37m"
echo Генерируем усреднённый файл для директории ""$2""
B=`genaverage "$2"`
printf %b "33[1;31mШаг 3/3n"
printf %b "33[1;37m"
echo Сравниваем усреднённые файлы
compare "$A" "$B"
rm -f $A $B
Положимся на режим -a, в котором iozone должен произвести в автоматическом режиме серию тестов, которые покрывают всевозможные операции с файлами разных размеров и разной длиной записи (моя версия произвела 1638 тестов).
В ходе эксперимента выяснилось, что одна серия тестов iozone может отличаться от другой по производительности на 5%, поэтому я не буду рассчитывать на результаты одного-единственного набора тестов и поступлю таким образом: проведу набор тестов несколько раз и сгенерирую файл с усредненными значениями по каждому из тестов (во всех расчётах данной статьи под усредненными значениями понимается среднеарифметическое значение). После этого я сравню усредненный файл, содержащий серию тестов по незашифрованной файловой системе с аналогичным файлом, содержащим серию тестов на зашифрованной ФС таким образом: каждый тест первого файла будет сравнен с соответствующим тестом второго в процентном соотношении, результаты сложим, а сумму разделим на количество тестов, получив среднеарифметическое значение, на сколько процентов первая группа тестов быстрее или медленнее второй.
Также, для простой оценки скорости, в дополнение к вышеописанным вычислениям, проведем простенький тест — копирование средствами dd 500 мегабайт из urandom на зашифрованную ФС блоками, равными размеру кластера ext4 по-умолчанию (4 килобайта). dd if=/dev/urandom of=testfile bs=4k count=128000. Шифровать будет раздел целиком (за исключением eCryptfs, которая этого делать не умеет). Я не буду приводить команды для каждого конкретного случая, покажу только результаты.
Итак, приступим:
1) Система шифрования: нет
Количество проведенных серий тестов: 14
Средняя разница в производительности между одинаковыми сериями тестов: 1,3%
Результат dd: 524288000 bytes (524 MB) copied, 187.823 s, 2.8 MB/s
2) Система шифрования: LUKS. Используя ключи по-умолчанию, наш том будет зашифрован так:
Cipher name: aes
Cipher mode: cbc-essiv:sha256
Hash spec: sha1
Количество проведенных серий тестов: 8
Средняя разница в производительности между одинаковыми сериями тестов: 1,4%
Результат dd: 524288000 bytes (524 MB) copied, 199.505 s, 2.6 MB/s
Падение производительности по сравнению с нешифрованной ФС: 7,9%
3) Система шифрования: Truecrypt. Применяя все параметры по-умолчанию, том будет зашифрован AES с алгоритмом хеширования RIPEMD-160.
Количество проведенных серий тестов: 22
Средняя разница в производительности между одинаковыми сериями тестов: 3,2%
Результат dd: 524288000 bytes (524 MB) copied, 199.719 s, 2.6 MB/s
Падение производительности по сравнению с нешифрованной ФС: 7%
4) Система шифрования: eCryptfs
aes: blocksize = 16; keysize = 16
Количество проведенных серий тестов: 9
Средняя разница в производительности между одинаковыми сериями тестов: 0,9%
Результат dd: 524288000 bytes (524 MB) copied, 199.624 s, 2.6 MB/s
Падение производительности по сравнению с нешифрованной ФС: 49,5%
Итог: производительность eCryptfs по всевозможным файловым операциям оставляет желать лучшего, хотя обычный тест dd с оптимальным размером блока показывает скорость, аналогичную остальным системам шифрования. Шифровать целиком ей домашнюю директорию я бы не стал. Удобно применять лишь при шифровании отдельных поддиректорий. Ожидания меньшей производительности Truecrypt из-за использования FUSE не оправдались, в моём случае он оказался даже немного быстрее LUKS.
В дополнение, как бонус, приведу аналогичный тест dd для Truecrypt и LUKS, но для file-hosted контейнера (все параметры шифрования аналогичны).
1) LUKS
524288000 bytes (524 MB) copied, 207.07 s, 2.5 MB/s
2) Truecrypt
524288000 bytes (524 MB) copied, 205.046 s, 2.6 MB/s
Падение производительности можно объяснить тем, что появляются дополнительные накладные расходы на чтение файла контейтера с уже существующей файловой системы вместо прямого обращения к разделу.
Автор: infd