Page 1 of 2

TLS (клиент)

Posted: Fri Sep 29, 2017 1:00 pm
by akron1
Недавно, 4 сентября, я начал разработку библиотеки TLS. Первую неделю я читал документацию и прикидывал, насколько реально это сделать. Затем начал кодировать. За основу я взял старую версию TLS (1.0), потом можно будет модернизировать до 1.2. До завершения еще далеко, но кое-что уже есть. Программа может подключиться к https-серверу, обменяться ключами, отправить прикладной запрос и прочитать ответ. Парсер сертификатов я пока не сделал, а просто захардкодил открытый ключ Википедии.

Надо запустить программу и ввести в консоли "en.wikipedia.org/wiki/Main_Page" или "en.wikipedia.org/wiki/KolibriOS" (регистр имеет значение). Программа обменяется ключами и отправит запрос

GET /wiki/??? HTTP/1.1
Host: en.wikipedia.org

Полученный ответ будет показан в консоли и сохранен в файле /tmp0/1/1.txt.

Пока программа работает довольно медленно: криптографические алгоритмы очень тяжелые, O7/11 тут не справляется. Вроде бы, где-то здесь были готовые алгоритмы на асме, я пока их не рассматривал, но в будущем, конечно, надо применить ассемблерные решения. Даже на Core i7 требуется ~3 сек для вычисления RSA. 3DES работает со скоростью 60-70 кбайт/c. Небыстро...

Кстати, сервер Википедии иногда, вместо нормального ответа возвращает предупреждение, что, дескать, ваш "браузер" (IE 8.0, Windows XP) устарел и поддержка 3DES прекращается. Я знал, что 3DES устарел, но думал, что он еще вполне поддерживается. Просто мне показалось, что 3DES проще сделать, чем AES. Но всё равно надо перейти на AES.

При первом запуске программы, соединение с сервером устанавливается не сразу, а примерно через минуту. Программа пишет "connecting..." а дальше надо ждать. Есть какая-то проблема с сокетами. При последующих запусках, когда сокеты "прогрелись", соединение устанавливается незамедлительно.

Максимальный размер ответа ограничен 4 Мб -- если больше, будет аварийная ситуация. Оперативной памяти программа тратит тоже немало (40-50 Мб), но это вроде несложно уменьшить.

Пока всё очень ненадежно и медленно. Но начало положено...

Так выглядит в NetSurf главная страница Википедии, которая была загружена этой программой.
1.PNG
1.PNG (154.55 KiB)
Viewed 9968 times
----
05-oct-2017

Увеличена скорость получения данных из сети.

Добавлены функции AES и SHA256.

список поддерживаемых шифронаборов:

TLS_RSA_WITH_3DES_EDE_CBC_SHA (устарел)
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_CBC_SHA256 (TLS 1.2)
TLS_RSA_WITH_AES_256_CBC_SHA256 (TLS 1.2)

последние два используются только с TLS 1.2, пока не нужно, но потом пригодятся.

Сервер перестал присылать сообщения об устаревшем 3DES. К тому же, AES работает намного быстрее.

----
15-oct-2017

Сделана обработка ошибок, оптимизация по памяти, улучшена внутренняя структура, готовится преобразование программы в библиотеку: убраны почти все глобальные переменные, сформирован интерфейс будущей библиотеки. Остается еще много мелких недоработок + парсер сертификатов.

----
22-oct-2017

Большой прогресс. Сделан простой парсер сертификатов (программа подключается только к Wikipedia, но теперь открытый ключ не встроен в программу, а извлекается из сертификата сервера). Предусмотрена возможность возобновления сессии. Сделана обработка сетевых событий. Снято ограничение на размер ответа. Повышена надежность работы программы. Оптимизирован алгоритм AES -- теперь на мощных машинах он работает в несколько раз быстрее, чем сетевая подсистема в KolibriOS.
Остается сделать кое-какую мелочь, преобразовать программу в библиотеку, написать примеры использования и описание функций.

Re: TLS (клиент)

Posted: Fri Sep 29, 2017 1:56 pm
by Leency
Очень полезное и серьезное начинание.

Re: TLS (клиент)

Posted: Fri Sep 29, 2017 4:54 pm
by baggacfreak
Good work! Glad to see that someone tackles it!

Re: TLS (клиент)

Posted: Fri Sep 29, 2017 6:49 pm
by IgorA
Когда-то я делал алгоритм шифрования des посмотреть можно здесь:
http://websvn.kolibrios.org/listing.php ... 162e2aad94
А алгоритм aes делал diamond для программы kfar:
http://websvn.kolibrios.org/filedetails ... %2Faes.inc

Re: TLS (клиент)

Posted: Fri Sep 29, 2017 8:22 pm
by hidnplayr

Re: TLS (клиент)

Posted: Fri Sep 29, 2017 9:00 pm
by akron1
hidnplayr,
Yes, I know about that work, but I don't code in asm.

Re: TLS (клиент)

Posted: Fri Sep 29, 2017 9:45 pm
by hidnplayr
What language did you write this in?
Do you care to share the source code? Maybe it can be used to improve ASM version :)

Re: TLS (клиент)

Posted: Fri Sep 29, 2017 10:22 pm
by akron1
Oberon-07/11. This compiler is another my work. Later the cryptographic algorithms will be replaced by those written in C (or asm). But it's not yet soon.
Yes, I'll share the source code. But it's still early.

Re: TLS (клиент)

Posted: Fri Sep 29, 2017 11:24 pm
by hidnplayr
Cool, keep up the good work!

Re: TLS (клиент)

Posted: Sat Sep 30, 2017 4:22 pm
by ashmew2
Hello akron1

This looks pretty useful for Netsurf as well to support HTTPS.

Maybe I'll be able to incorporate it into Netsurf once this is matured enough.

Re: TLS (клиент)

Posted: Sat Sep 30, 2017 6:13 pm
by akron1
ashmew2

I hope it will be possible. I continue to develop...

Re: TLS (клиент)

Posted: Sat Oct 28, 2017 3:16 pm
by akron1
Программа преобразована в библиотеку. Пока еще есть недоработки, но уже можно пробовать ее использовать.

Вероятно, есть проблемы с сетевой подсистемой KolibriOS: иногда библиотека не может открыть сокет и возвращает код ошибки -23. Библиотека сделана с возможностью компиляции как для KolibriOS, так и для Windows (я вынес все системные зависимости в один модуль). Компиляция для Windows мне нужна для упрощения разработки и тестирования. Поэтому библиотека содержит небольшую избыточность. В Windows всё работает безотказно. В KolibriOS -- то не открывается сокет, то теряется соединение.

Пока я возьму паузу и продолжу после нового года. Планирую сделать:

- поддержку TLS 1.1 и 1.2
- заменить криптоалгоритмы на высокоэффективные, написанные на асме или C (прежде всего RSA)
- улучшить надежность работы
- возможно переписать отдельные части или даже всё. Я впервые писал сетевое приложение и многие особенности работы с сетью мне были неизвестны. И поэтому, то, что я написал мне не нравится. Теперь, когда есть работающая реализация и получен опыт, написать новую будет несложно.

Описание функций
Spoiler:void lib_init (void)

инициализировать библиотеку.

Приложение обязательно должно вызвать эту функцию после загрузки библиотеки.

______________
void *open (void *session, int b1, int b2, int b3, int b4, int port, int buf_size, int &error)

открыть сессию

- session -- указатель на session_id и master_secret ранее открытой сессии или 0, если эта сессия первая
- b1, b2, b3, b4 -- IP сервера
- port -- порт (для https port = 443)
- buf_size -- размер буфера для временного хранения данных до передачи в приложение. Приложение должно
читать данные как можно быстрее, иначе буфер будет заполнен, приём данных от сервера остановится и
соединение может быть потеряно. Это не проблема библиотеки -- при работе под Windows, даже если приложение
читает данные медленно, соединение не теряется. В настоящее время, допустимый размер буфера 256 Кб - 256 Мб.
На самом деле, библиотека создает два буфера заданного размера. Буферы кольцевые: если достигнут конец
буфера, то данные будут записаны в начало, если начало уже прочитано приложением. При быстрой работе
приложения и медленной работе сети, переполнение буферов не происходит.
- error -- возвращаемый код ошибки (отрицательное число), 0 -- успех.

результат: указатель на параметры соединения, 0 -- ошибка

______________
void session (void *params, void *buffer)

получить session_id и master_secret ранее открытой (и еще не закрытой!) сессии

- params -- указатель на параметры соединения ранее открытой сессии
- buffer -- адрес памяти, куда будет записан результат (81 байт минимум). Это можно использовать для открытия
следующей сессии с тем же сервером (функция open, параметр session), чтобы повторно не вычислять RSA
(алгоритм RSA работает медленно).

______________
void close (void **params)

закрыть сессию, освободить память

- params -- указатель на параметры соединения. Будет присвоено значение 0.

______________
int send (void *params, void *buffer, int size)

отправить данные

- params -- указатель на параметры соединения
- buffer -- указатель на данные
- size -- размер данных

результат: 0 -- успех, иначе код ошибки

______________
int recv (void *params, void *buffer, int size)

получить данные

- params -- указатель на параметры соединения
- buffer -- указатель на буфер для данных
- size -- размер буфера

результат:

>= 0 -- количество записанных в буфер байтов.
-17 -- сервер завершил передачу данных и закрыл сессию. После этого необходимо вызвать close,
чтобы освободить память.
< 0 (!= -17) -- код ошибки.

______________
Общий порядок вызовов:

open
session (необязательно)
send
recv
close


Cоглашение вызовов stdcall.
Рекомендуемый минимальный размер стэка приложения 256 Кб.
----
03-nov-2017

До перерыва, я всё же сделал одно небольшое, но важное улучшение. Я переделал алгоритм RSA (изменил форму представления длинных чисел и сделал предварительные вычисления). Теперь RSA работает в 7 раз быстрее. На Core i7 время работы алгоритма сократилось с 2250 мс до 320 мс. Приложению требуется дополнительно 64 Кб стэка для хранения результатов предварительных вычислений.

Re: TLS (клиент)

Posted: Wed Nov 01, 2017 12:11 pm
by hidnplayr
Very nice work, I will try to connect it to HTTP library in the future and see how usable it can be.

Re: TLS (клиент)

Posted: Thu Nov 16, 2017 4:20 pm
by akron1
Всё же, учитывая сравнительно малый объем оставшейся работы по этой библиотеке, я решил не откладывая довести программу до предварительного завершения.

- Изменен интерфейс
- Исправлены баги
- Добавлена поддержка TLS 1.1

Тщательного тестирования я не проводил, но простые тесты программа проходит.

Я раньше писал о нестабильной работе билиотеки в KolibriOS по сравнению с Windows. Теперь можно описать проблемы более точно:

- "не открывается сокет". На самом деле сокет открывается, просто в некоторых случаях функция 75.4 не может установить соединение с сервером. После неудачного вызова f75.4, регистр ebx содержит значение 60. Если это код ошибки, то "Connection timeout". Решение: в случае неудачи, библиотека пробует подключиться еще 4 раза, то есть, делается до пяти попыток соединения, обычно удается подключиться со второй попытки, если не удалось с первой.

- "теряется соединение". Теряется не соединение, а данные, если буфер сокета был переполнен. Это может произойти, когда приложение обрабатывает данные медленнее, чем они поступают из сети. Не знаю, баг ли это в реализации протокола TCP, или просто Windows использует буфер сокета большего размера.

Новый интерфейс библиотеки. Вместо указателей на параметры соединения, используются целочисленные идентификаторы для защиты памяти.
Spoiler:void lib_init (void)

инициализировать библиотеку.

Приложение обязательно должно вызвать эту функцию после загрузки библиотеки.

-------------------------------------------
int open (void *session, int b1, int b2, int b3, int b4, int port, int buf_size, int &error)

открыть сессию

- session -- указатель на session_id и master_secret ранее открытой сессии или 0, если эта сессия первая
- b1, b2, b3, b4 -- IP сервера
- port -- порт (для https port = 443)
- buf_size -- размер буфера для временного хранения данных до передачи в приложение. Приложение должно
читать данные как можно быстрее, иначе буфер будет заполнен, и данные будут потеряны.
Это не проблема библиотеки -- при работе в Windows, даже если приложение читает данные медленно,
потеря данных не происходит. В настоящее время, допустимый размер буфера 256 Кб - 256 Мб.
На самом деле, библиотека создает два буфера заданного размера. Буферы кольцевые: если достигнут конец
буфера, то данные будут записаны в начало, если начало уже прочитано приложением. При быстрой работе
приложения и медленной работе сети, переполнение буферов не происходит.
- error -- возвращаемый код ошибки (отрицательное число), 0 -- успех.

результат: идентификатор соединения, 0 -- ошибка

-------------------------------------------
bool session (int connection, void *buffer)

получить session_id и master_secret ранее открытой (и еще не закрытой!) сессии

- connection -- идентификатор соединения ранее открытой сессии
- buffer -- адрес памяти, куда будет записан результат (81 байт). Это можно использовать для открытия
следующей сессии с тем же сервером (функция open, параметр session), чтобы повторно не вычислять RSA
(алгоритм RSA работает медленно).

результат: true -- успех, false -- ошибка

-------------------------------------------
bool close (int connection)

закрыть сессию, освободить память

- connection -- идентификатор соединения.

результат: true -- успех, false -- ошибка (неправильный идентификатор или соединение уже закрыто)

-------------------------------------------
int send (int connection, void *buffer, int size)

отправить данные

- connection -- идентификатор соединения
- buffer -- указатель на данные
- size -- размер данных

результат: 0 -- успех, иначе код ошибки

-------------------------------------------
int recv (int connection, void *buffer, int size)

получить данные

- connection -- идентификатор соединения
- buffer -- указатель на буфер для данных
- size -- размер буфера

результат:

>= 0 -- количество записанных в буфер байтов.
== -17 -- сервер завершил передачу данных и закрыл сессию.
< 0 (!= -17) -- код ошибки.

-------------------------------------------
Общий порядок вызовов:

open
session (необязательно)
send
recv
close


Cоглашение вызовов stdcall.
Рекомендуемый размер стэка приложения 256 Кб.
Список кодов ошибок. На практике, многие ошибки никогда не возникают, если сервер работает правильно. Если ошибку возвращает функция open, то ничего делать не надо. Если код ошибки (в том числе и код -17) возвращают функции send или recv, то после этого надо вызвать функцию close чтобы освободить память.
Spoiler:-1 не удалось отправить данные
-2 ошибка дешифрования записи
-3 неправильный MAC записи
-4 время истекло, сервер не ответил
-5 ошибка версии протокола TLS
-6 запись переполнена
-7 отказ согласования
-8 не удалось открыть сокет
-9 получено сообщение alert
-10 неожиданный тип записи
-11 отказ возобновления сессии
-12 недопустимый шифронабор
-13 заполнен буфер для handshake-сообщений
-14 тип ключа не соответствует шифронабору
-15 ошибка парсера сертификатов
-16 слишком много соединений
-17 сервер завершил передачу данных и закрыл сессию
-18 слишком длинный ключ RSA
-19 слишком короткий ключ RSA
-20 параметр size функции send меньше нуля
-21 неправильный идентификатор соединения
-22 соединение разорвано
-23 не удалось создать процесс (для сокета)
-24 не удалось создать процесс (для дешифрования)
-25 недостаточно памяти
-26 не удалось соединиться с сервером
Поддержку TLS 1.2 и оптимизацию криптографических алгоритмов пока можно отложить.
Если будут какие-либо рациональные предложения по улучшению, изменению интерфейса и т. п., то я, по-возможности, сделаю. А пока на этом всё.

Re: TLS (клиент)

Posted: Fri Nov 17, 2017 12:48 am
by Siemargl
very nice

отличная заделка на будущее. грац