LINUX.ORG.RU

Шифрование раздела вручную

 , ,


0

1

Доброго времени суток!

Возникла задача зашифровать раздел «своими руками». Алгоритм шифрования не так важен, поэтому пусть будет простейший, например, XOR.

Написал программу на C++:

#include <iostream>
#include <fstream>

int main()
{
	std::fstream filestream;

	const char key = 0b11111111;

	filestream.rdbuf()->pubsetbuf(0, 0);
	filestream.open("/dev/sdc1",  std::ios_base::binary | std::ios_base::in | std::ios_base::out);

	char b;

	while (filestream.read(&b, 1))
	{
		b ^= key;
		filestream.seekp((int)filestream.tellp() - 1);
		filestream.write(&b, 1);
	}

	filestream.close();

	return 0;
}

Запускать, очевидно, из-под рута. Она работает, и девайс шифруется, но это длится крайне долго даже для флешки объёмом 2Гб.

Подскажите, есть ли более быстрый способ выполнить это? Или просто, как бы это сделали вы.

Спасибо!

очевидно, читать не по одному байту.

почитай роберта лава, для начала

Или просто, как бы это сделали вы

использовал бы dm-crypt

vvviperrr ★★★★★
()
Последнее исправление: vvviperrr (всего исправлений: 1)
Ответ на: комментарий от vvviperrr

Спасибо за ответ!

использовал бы dm-crypt

Без использования dm-crypt.

очевидно, читать не по одному байту.

А в чём выгода?

почитай роберта лава, для начала

Какую книгу?

AccumPlus
() автор топика

даже для флешки объёмом 2Гб

Не даже, а именно. Тормозная флешка.

no-such-file ★★★★★
()
Ответ на: комментарий от anonymous

Спасибо за ответ!

В производительности. Читать нужно минимум один сектор устройства.

Понял, логично.

AccumPlus
() автор топика
Ответ на: комментарий от anonymous

Читать нужно минимум один сектор устройства

Вообще-то физически невозможно читать меньше чем сектор устройства и буферный кэш для блочных устройств никто не отменял.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Вообще-то физически невозможно читать меньше чем сектор устройства

Это значит, что для своего байта я каждый раз запрашиваю сектор, верно?

AccumPlus
() автор топика
Ответ на: комментарий от no-such-file
pve ~ # dd if=/dev/sda of=/dev/null bs=1 count=134217728
134217728+0 records in
134217728+0 records out
134217728 bytes (134 MB) copied, 33.92 s, 4.0 MB/s
pve ~ # echo 3 >/proc/sys/vm/drop_caches
pve ~ # dd if=/dev/sda of=/dev/null bs=512 count=262144
262144+0 records in
262144+0 records out
134217728 bytes (134 MB) copied, 0.254056 s, 528 MB/s

Надеюсь, твоя картина мира не рухнула.

anonymous
()
Ответ на: комментарий от anonymous

А если выхлоп dd пайпнуть в openssl, то можно избавиться от кода из шапки. Юзай криптосойкие алгоритмы, Люк!

anonymous
()
Ответ на: комментарий от anonymous
# echo 3 >/proc/sys/vm/drop_caches
# grep sda /proc/diskstats
   8       0 sda 86209 9418 4662975 469532 65965 149750 3624105 758932 0 479332 1228340
# dd if=/dev/sda of=/dev/null bs=1 count=512
# grep sda /proc/diskstats
   8       0 sda 86210 9418 4663007 469552 65966 149750 3624121 758932 0 479352 1228360

Было 86209 чтений, стало 86210. Сектор прочитался с девайса 1 раз. Тебя не смущает, что скорость чтения пропорциональна bs даже на закэшированных данных?

# dd if=/dev/zero of=/dev/null bs=1 count=134217728
134217728+0 записей получено
134217728+0 записей отправлено
134217728 байт (134 MB, 128 MiB) скопирован, 62,9827 s, 2,1 MB/s

# dd if=/dev/zero of=/dev/null bs=512 count=262144
262144+0 записей получено
262144+0 записей отправлено
134217728 байт (134 MB, 128 MiB) скопирован, 0,139485 s, 962 MB/s

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от no-such-file

Без кэша было бы медленнее, однако один байт тоже крайне медленно из буфера кэша. Представь сколько лишних действий - и так для каждого байта.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от no-such-file

Не пойму, что ты пытаешься сказать. Вопрос был по производительности. Чтобы она была нормальной, нет смысла читать за раз меньше, чем /sys/block/sd?/queue/minimum_io_size. Твой кэш тут очевидно никак не спасает.

anonymous
()

И ещё... Может быть стоило бы создать для этого отдельную тему, но вопрос, в какой момент лучше зашифровать root-раздел?

AccumPlus
() автор топика

Если ты огласишь свою истинную цель, можно будет подобрать для тебя вариант намного быстрее. Если это домашнее задание или лаба, то и так прокатит. Если нужно сделать по-человечески - dm-crypt. Если просто хочется извращений и нечего делать, то стоит хотя бы читать и скармливать криптомодулю данные блоками (про оптимальный размер блока хотя бы тут), шифровать параллельно в отдельном треде (если шифрование будет сложным), писать на диск обратно в предыдущем треде, и уж наверняка не через тормозные плюсовые стримы.

jcd ★★★★★
()
Последнее исправление: jcd (всего исправлений: 1)
Ответ на: комментарий от jcd

Не стоит мерить других по себе. Все люди касаются тех или иных задач в разное время, согласитесь. Если Вы изучаете Линукс со школы, то я его нормально коснулся только пол года назад.

AccumPlus
() автор топика
Ответ на: комментарий от jcd

Истиная цель связана с заданием на моей работе.

Короче говоря, нужен аналог TrueCrypt и ему подобных, но написанный своими руками.

Сейчас я занимаюсь просто изучением этого методом проб и ошибок. По аналогии с TrueCrypt написал device-mapper target драйвер, который налету (де)шифрует устройства. Для проверки создавал образ файловой системы. Когда заработало, решил попробовать на разделе.

Начал с флешки. Заметил, что шифруется долго. Подумал, что что-то не понимаю - написал сюда. Вот такой алгоритм.

AccumPlus
() автор топика
Ответ на: комментарий от anonymous

И про ключи нельзя забывать - им не место в оперативке.

Да да, это просто пример

AccumPlus
() автор топика
Ответ на: комментарий от AccumPlus

В общем да, но без учёта твоего «криптографического» алгоритма это уже пустые спекуляции.

anonymous
()
Ответ на: комментарий от anonymous

TRESOR Runs Encryption Securely Outside RAM

Current disk encryption techniques store necessary keys in RAM and are therefore susceptible to attacks that tar- get volatile memory, such as Firewire and cold boot at- tacks. We present TRESOR, a Linux kernel patch that implements the AES encryption algorithm and its key management solely on the microprocessor. Instead of us- ing RAM, TRESOR ensures that all encryption states as well as the secret key and any part of it are only stored in processor registers throughout the operational time of the system, thereby substantially increasing its security. Our solution takes advantage of Intel’s new AES-NI in- struction set and exploits the x86 debug registers in a non-standard way, namely as cryptographic key storage. TRESOR is compatible with all modern Linux distribu- tions, and its performance is on a par with that of stan- dard AES implementations.

anonymous
()
Ответ на: комментарий от anonymous

Интересно, но

exploits the x86 debug registers in a non-standard way

как-то напрягает. Не уверен, что это будет в ядре.

anonymous
()
Ответ на: комментарий от AccumPlus

филфака?
тянет на 2 курс профильного. ну или 3 если часть большей задачи.

хотя, какая разница, доки ты все равно не читаешь (пытаться шифровать руками, никогда не слышав словосочетание «блочный шифр»?), тут хоть MIT хоть ПТУ.

mos ★★☆☆☆
()
Ответ на: комментарий от mos

Я рад, что Вы разбираетесь в этом лучше меня. Ведь для того и существует форум, верно? ;)

AccumPlus
() автор топика
Ответ на: комментарий от slackwarrior

Даже три раза запущу - виртуалку не жалко

AccumPlus
() автор топика
Ответ на: комментарий от AccumPlus

Не знаю, как можно доучиться до второго курса магистратуры профильного и при этом читать файлы побайтно. Короче, стоит расчехлить mmap(), читать блоками (дополнительно написав алгоритм, экспериментирующий с размерами и находящий более-менее оптимальный в зависимости от времени раундтрипа чтения-записи, взяв за начальный, скажем, 4096 или 8192), блоки передавать в блочный шифр, предварительно доразбив их до нужной длины. Один тред под IO, другой под шифрование. Синхронизация, скажем, по наличию определённого количества готового материала для записи (например если оптимальный дисковый буфер 4096, используем AES - размер блока 128, то условие записи - накопление числа шифрованных блоков, кратных 32, или если файл кончился, итд).

jcd ★★★★★
()
Ответ на: комментарий от jcd

Спасибо за ответ!

Не знаю, как можно доучиться до второго курса магистратуры профильного и при этом читать файлы побайтно

Вы удивитесь, но большинство не может и этого. И, возможно, удивитесь ещё больше, но профильное образование совсем не обязательно даёт инфу по блочным шифрам.

читать блоками

Можете привести пример на языке C/C++? Спасибо!

AccumPlus
() автор топика
Ответ на: комментарий от AccumPlus

Примеров работы с mmap() на сишке в интернетах вагон, выбирай любой, там всё элементарно. Не забудь только про нюанс с размером mmap-страницы - стоит «читать» (memcpy) за один раз блок размером не меньше getpagesize(), после чего разбивать на криптуемые блоки и отправлять в криптор. После «записи» (memcpy из криптора) - делать munmap() и в цикле делать следующий mmap() с offset+=pagesize. Кстати, если размер диска у тебя не кратен размеру криптуемого блока (для AES - 16 байт, в предыдущем посте у меня косяк)- не забудь реализовать что-то вроде XTS.

jcd ★★★★★
()
Последнее исправление: jcd (всего исправлений: 1)
Ответ на: комментарий от AccumPlus

Не за что. Как напишешь - черкни конкретную циферку, любопытно - во сколько сотен раз подскочит перфоманс по сравнению с первым вариантом.

jcd ★★★★★
()
Ответ на: комментарий от jcd

Кажется, что я уже начал делать что-то неправильно. Если что, можно кидать камнями).

Сделал пока так (без блочных шифров, и убрал все обработки ошибок)

На сях с map:

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <sys/mman.h>

int main()
{
	const char key = 0xFF;

	int fd = open("sample", O_RDWR);

	long filesize = lseek(fd, 0, 2);

	char *map;

	int pagesize = getpagesize();

	long i = 0;
	int j;

	struct timespec start, end;
	clock_gettime(CLOCK_MONOTONIC_RAW, &start);

	while (i * pagesize < filesize)
	{
		map = (char*)mmap(0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, i++ * pagesize);

		for (j = 0; j < pagesize; ++j)
			map[j] ^= key;

		msync(map, pagesize, MS_SYNC);
		munmap(map, pagesize);
	}

	clock_gettime(CLOCK_MONOTONIC_RAW, &end);

	close(fd);
	
	printf("Time: %ld\n", (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000);

	return 0;
}

На плюсах:

#include <iostream>
#include <fstream>
#include <sys/time.h>
#include <time.h>

int main()
{
	std::fstream filestream;

	const char key = 0b11111111;

	filestream.rdbuf()->pubsetbuf(0, 0);
	filestream.open("sample",  std::ios_base::binary | std::ios_base::in | std::ios_base::out);

	char b;

	struct timespec start, end;
	clock_gettime(CLOCK_MONOTONIC_RAW, &start);

	while (filestream.read(&b, 1))
	{
		b ^= key;
		filestream.seekp((int)filestream.tellp() - 1);
		filestream.write(&b, 1);
	}
	clock_gettime(CLOCK_MONOTONIC_RAW, &end);

	filestream.close();

	printf("Time: %ld\n", (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000);

	return 0;
}

В настоящий момент тестирую результаты на флешке. Но пробовал делать над обычными файлами - сильно выигрывает C++. Это конечно текстовые файлы, но всё равно.

Для 10Мбайтного файла:

Код на C: 21,8 сек

Код на С++: 8,6 сек

Специально запускал несколько раз, чтобы буферизация была одинакова для обоих процессов.

Я почти уверен, что на сях написал не то, что вы хотели (вы говорили про memcpy, а я его здесь вообще не использую).

AccumPlus
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.