Этот документ основан на tor-spec.txt
$Id: tor-spec.txt 11207 2007-08-19 21:54:20Z arma $
Roger Dingledine, Nick Mathewson
Спецификация протокола Tor
! | Этот документ содержит спецификации Tor для версий 0.1.2.x или более ранних. Будущие версии Tor'а могут применять усовершенствованные протоколы, и совместимость не гарантируется. |
Эта спецификация не является описанием разработки; причины выбора тех или иных параметров в ней не отражены. Дополнительную информацию почему Tor устроен именно так, а не иначе, смотри в tor-design.pdf.
0. Вступление
0.1. Символы и обозначения
- PK — открытый ключ.
- SK — секретный ключ.
- K — ключ для симметричного шифрования.
- a|b — конкатенация 'a' и 'b'.
- [A0 B1 C2] — трехбайтовая последовательность, содержащая байты с шестнадцатиричными значениями A0, B1 и C2, в указанном порядке.
Все числовые значения кодируются в сетевом (big-endian) порядке.
- H(m) — криптографический хэш от m.
0.2. Параметры безопасности
Tor использует потоковый шифр, шифр с открытым ключом, протокол Диффи-Хеллмана, и хэш-функцию.
- KEY_LEN — длина ключа потокового шифра, в байтах.
- PK_ENC_LEN — длина сообщения, зашифрованного открытым ключом, в байтах.
- PK_PAD_LEN — число байт, добавляемых перед шифрованием с открытым ключом, в байтах.
(Таким образом наибольшее число байт, которое может быть зашифровано в одну операцию с открытым ключом составляет PK_ENC_LEN-PK_PAD_LEN.)
- DH_LEN — число байт используемых для представления члена группы Диффи-Хеллмана.
- DH_SEC_LEN — число байт используемых для секретного ключа (x) Диффи-Хеллмана.
- HASH_LEN — длина выхода хэш-функции, в байтах.
- PAYLOAD_LEN — наибольшая длина полезных данных в пакете, в байтах. (509)
- CELL_LEN — длина пакета Tor, в байтах.
0.3. Шифры.
В качестве потокового шифра, мы используем 128-бит AES в режиме счетчика, все байты вектора инициализации IV устанавливаются в 0.
В качестве шифра с открытым ключом мы используем RSA с ключом 1024 бита и фиксированной экспонентой 65537. Мы используем OAEP-MGF1 дополнение, с SHA-1 в качестве хэш-функции. Мы оставили опциональный параметр "Label" неустановленным. (Подробно OAEP дополнение смотри ftp://ftp.rsasecurity.com/pub/.....kcs-1/pkcs-1v2-1.pdf)
[Nick, как получилось, что "мы оставили опциональный параметр Label неустановленным"? -RD]
Для схемы Диффи-Хеллмана, мы используем генератор (g) равный 2. В качестве модуля §, мы используем 1024-битное безопасное простое число из rfc2409 секция 6.2, шестнадцатиричное представление которого:
В целях оптимизации, приложения должны выбирать DH секретный ключ (x) длиной 320 бит. Приложения никогда не должны использовать никаких более длинных DH ключей.
[Могут другие приложения использовать свои DH ключи повторно?? -RD]
[Вероятно нет. В принципе, этого можно избежать, меняя ключи DH раз в секунду, но, на мой взгляд, здесь слишком много неочевидных атак, чтобы чувствовать себя спокойным. -NM]
В качестве хэш-функции мы используем SHA-1.
KEY_LEN=16.
DH_LEN=128; DH_SEC_LEN=40.
PK_ENC_LEN=128; PK_PAD_LEN=42.
HASH_LEN=20.
Когда мы говорим "хэш открытого ключа", мы имеем ввиду SHA-1 хэш DER encoding открытого ключа ASN.1 RSA (как указано в PKCS.1).
Все "случайные" значения должны быть сгенерированы с помощью криптографически стойкого генератора случайных чисел, если не указано иначе.
"Гибридное шифрование" байтовой последовательности М открытым ключом PK производится следующим образом:
- Если М короче, чем PK_ENC_LEN-PK_PAD_LEN, дополняем и шифруем М с PK.
- В противном случае генерируем случайный ключ К длиной KEY_LEN байт.
Пусть M1 = первые PK_ENC_LEN-PK_PAD_LEN-KEY_LEN байтов M, пусть M2 = остаток M.
Дополняем и шифруем K|M1 с PK. Шифруем M2 нашим потоковым шифром, используя ключ K. Объединяем получившиеся зашифрованные значения.
! | [XXX Важно: "гибридное шифрование" не мешает атакующему добавлять или удалять байты в конце сообщения M. Атакующий также может изменять байты, которые не защищены OAEP — подробности смотри в Goldberg's PET2006 paper. Впоследствии мы собираемся добавить MAC в эту схему. -RD] |
0.4. Значения других параметров.
CELL_LEN=512
1. Краткий обзор системы.
Tor — это распределенная сеть, разработанная для анонимизации основанных на TCP приложений, требующих малых задержек, таких как просмотр веб-страниц, secure shell, и системы мгновенной передачи сообщений. Клиенты выбирают путь в сети и строят цепочку, в которой каждый узел (или "луковичный маршрутизатор", "onion router" далее "OR") в пути знает только своего предшественника и преемника, но никаких других узлов цепочки. Траффик следует по цепочке в виде пакетов стандартного размера, с которых каждый узел снимает свое симметричное шифрование (как снятие слоя с луковицы) и передает дальше.
1.1. Ключи и имена.
Каждый сервер Tor имеет несколько пар открытых/закрых ключей:
- Долговременный, "только для подписи", "Identity key", предназначен для подписания документов и сертификатов, и используется для идентификации сервера.
- Среднесрочные "Onion key" используются для расшифрования "слоев луковицы" когда предпринимаются попытки расширить цепочку. (См. 5.1.) Старые ключи должны приниматься как минимум в течении одной недели после того, как они сняты с рассылки. Поэтому серверы обязаны хранить старые ключи некоторое время после их замены.
- Краткосрочные "Connection key" используются для установки TLS соединения. Приложения Tor могут заменять эти ключи так часто, как хотят, и должны заменять эти ключи не реже раза в сутки.
Сервера Tor также идентифицируются по "псевдонимам"; они специфицированы в dir-spec.txt.
2. Соединения
Tor использует TLS/SSLv3 для сетевой аутентификации и шифрования. Все приложения обязаны поддерживать SSLv3 схему шифрования "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", также должны поддерживать TLS схему шифрования "TLS_DHE_RSA_WITH_AES_128_CBC_SHA" если это возможно. Приложения могут поддерживать другие схемы шифрования TLS, но не должны поддерживать никаких схем, в которых отсутствуют сеансовые ключи, или в которых длина ключа симметричного шифрования меньше чем KEY_LEN бит, или длина хэша меньше чем HASH_LEN бит. Приложения не должны разрешать никакие другие схемы шифрования SSLv3.
Хотя протоколы соединений одинаковы, мы будем различать их инициаторов как "луковичные маршрутизаторы" (OR), если они хотят передавать трафик для других пользователей Tor, или как "луковичные прокси" (OP) если они обрабатывают только локальные запросы. OP не должны использовать никаких долговременно отслеживаемых идентификаторов в их протоколах взаимной идентификации (схемах ключевого соглашения).
Во время ключевого соглашения TLS инициатор соединения всегда посылает цепочку из двух сертификатов: первый сертификат X.509, использующий сеансовый открытый ключ, и второй, самоподписанный X.509 сертификат, содержащий его ключ идентификации. "commonName" первого сертификата это "псевдоним" OR, "commonName" второго сертификата это "псевдоним" OR, за которым следует пробел и строка "<identity>". Другая сторона посылает подобную цепочку сертификатов.
Приложения, использующие Протокол 1 или более ранние, используют значение "organizationName" "Tor" или "TOR". Будущие приложения (которые поддерживают версию протокола, описанную в секции 4.1) не должны использовать ни одно из этих значений для своего "organizationName".
Все стороны, принимающие сертификаты, должны подтвердить, что ключи идентификации те, которые ожидаются. (При инициализации соединения ожидаемый ключ идентификации – один из корневых; при создании подключения пакетом EXTEND, ожидаемый ключ идентификации – один из заданных в пакете. Если ключ не относится к ожидаемым, сторона должна закрыть соединение.
При подключении к OR все стороны должны отклонять соединение, если сертификат OR неправильно оформлен или отсутствует. При приеме входящего соединения OR не должен отклонять входящее соединение, даже если сертификат другой стороны неправильно оформлен или отсутствует. (Однако OR не должен предполагать, что получил входящее соединение от другого OR, даже если сертификат присутствует и правильно оформлен.)
[До версии 0.1.2.8-rc, ORы отвергали входящие соединения от ORов и OPов если их сертификаты отсутствовали или были неправильно оформлены.]
Когда TLS соединение установлено, обе стороны посылают пакеты (специфицированные ниже) друг другу. Пакеты посылаются последовательно. Все пакеты имеют длину CELL_LEN байт. Пакеты могут быть встроены в TLS записи любой длины или поделены между TLS записями. Но кадрирование TLS записей не должно допускать никакой утечки информации о типе или содержании пакетов.
TLS соединение не постоянно. Любая сторона может закрыть соединение, если не было никакого обмена информацией в течение заданного периода времени ("KeepalivePeriod", по умолчанию 5 минут)
(Как исключение корневые сервера могут пытаться оставаться подключенными ко всем OR — но это будет постепенно убираться в версиях Tor 0.1.2.x)
3. Формат пакетов.
Базовая единица коммуникации для OR и OP это фиксированных размеров "пакет".
В версии 1 соединения каждый пакет содержит следующие поля:
Command — [1 байт]
Payload — (дополненный байтами "0") [PAYLOAD_LEN байт]
Поле CircID определяет, с какой из имеющихся цепочек связан пакет.
Поле 'Command' содержит одно из следующих значений:
- 0 — PADDING (дополнение)(См. секц. 7.2)
- 1 — CREATE (Создать цепочку)(См. секц. 5.1)
- 2 — CREATED (Подтверждение создания)(См. секц. 5.1)
- 3 — RELAY (Сквозные данные) (См. секц. 5.5 и 6)
- 4 — DESTROY (Остановить использование цепочки) (См. секц. 5.4)
- 5 — CREATE_FAST (Создать цепочку без PK) (См. секц. 5.1)
- 6 — CREATED_FAST (цепочка создана, без PK) (См. секц. 5.1)
Интерпретация 'Payload' зависит от типа ячейки.
- PADDING: Данные не используются.
- CREATE: Данные содержат предложение ключевого соглашения.
- CREATED: Данные содержат ответ на ключевое соглашение.
- RELAY:Данные содержат заголовок и тело передаваемой информации.
- DESTROY: Данные содержат причину закрытия цепочки. (См. 5.4)
При получении любого другого значения поля команды OR должен отбросить пакет.
[XXXX Версии до 0.1.0.?? писали в лог предупреждение при отбрасывании пакета; это было неправильно. -NM]
Данные дополняются нулями.
Пакет PADDING в настоящее время применяется для поддержания соединения. Если нет никакого другого траффика, ORы и OPы посылают друг другу PADDING пакет каждые несколько минут.
CREATE, CREATED и DESTROY пакеты используются для управления цепочкой; смотри секцию 4 ниже.
RELAY пакеты используются для того чтобы пересылать команды и данные по цепочке; смотри секцию 5 ниже.
4. [Этот раздел преднамеренно оставлен пустым.]
5. Управление цепочкой.
5.1. Пакеты CREATE и CREATED.
Пользователи создают цепочки наращивая их на один переход за раз. Для создания новой цепочки, OPы посылают пакет CREATE первому узлу, с первой половиной ключевого соглашения DH; этот узел отвечает пакетом CREATED со второй половиной ключевого соглашения DH плюс первые 20 байт данных производного ключа (смотри секцию 5.2). Для расширения цепочки на следующий переход, OP посылает пакет EXTEND (смотри секцию 5) который дает последнему узлу цепочки указание послать пакет CREATE для расширения цепочки.
Данные для пакета CREATE это "кожица лука", которая состоит из данных первого шага ключевого соглашения DH (gx). Это значение гибридно зашифровано с использованием "onion key" получателя и имеет следующую структуру:
- Зашифровано открытым ключом PK:
Дополнение [PK_PAD_LEN байт]
Симметричный ключ [KEY_LEN байт]
Первая часть gx [PK_ENC_LEN-PK_PAD_LEN-KEY_LEN байт]
- Зашифровано симметричным алгоритмом:
Вторая часть gx [DH_LEN-(PK_ENC_LEN-PK_PAD_LEN-KEY_LEN) байт]
Данные для соотвествующего пакета EXTEND состоят из:
Порт [2 байта]
"Кожица лука" [DH_LEN+KEY_LEN+PK_PAD_LEN байт]
Отпечаток идентификатора [HASH_LEN байт]
Поля "Порт" и "Адрес" содержат IPV4 адрес и порт следующего маршрутизатора OR в цепочке; отпечаток идентификатора это хэш открытого ключа проверки подписи (закодированного как указано в PKSC#1 ASN1) следующего OR в цепочке. (См. 0.3) (Включение этого хэша позволяет OR, осуществляющему расширение, проверить, что он действительно соединился с указанным OR, и предотвращает некоторые атаки типа "человек посередине").
Данные для пакета CREATED, или соотвествующие данные для пакета EXTENDED содержат:
Данные производного ключа (KH) [HASH_LEN байт] (см. 5.2 ниже)
Поле CircID для пакета CREATE выбирается узлом (OR или OP), посылающим пакет CREATE, как случайное двухбайтовое целое. Для предотвращения CircID коллизий, когда один узел посылает пакет CREATE другому он выбирает только из половины возможных значений, основанных на открытых ключах подписи OR: если отправляющий узел имеет меньший ключ, он выбирает CircID с старшим значащим битом "0", в противоположном случае выбирается CircID с MSB "1".
(OPы не имеющие открытого ключа могут выбирать любое CircID по желанию, так как OPы никогда не исполняют пакеты CREATE.)
Открытые ключи сравниваются как числа по модулю.
Как обычно для DH, x и y должны генерироваться случайным образом.
[Для обеспечения обратной совместимости версий протоколов стороны должны отбрасывать пакеты CREATE в которых "кожица лука" состоит из одних нулей.]
5.1.1. Пакеты CREATE_FAST/CREATED_FAST.
Когда первый переход цепочки инициализирован, OP уже идентифицировал OR и выработал общий секретный ключ с использованием TLS. Поэтому не всегда необходимо для OP выполнять операции с открытым ключом для создания цепочки. В этом случае OP может послать пакет CREATE_FAST вместо пакета CREATE (только для первого перехода). OR отвечает пакетом CREATED_FAST и цепочка создана.
Пакет CREATE_FAST содержит:
Ключевой материал (X) [HASH_LEN байт]
Пакет CREATED_FAST содержит:
Данные производного ключа [HASH_LEN bytes] (См. 5.2 ниже)
Значения X и Y должны быть сгенерированы случайным образом.
[Версии Tor'а до 0.1.0.6-rc не поддерживают эти типы пакетов; клиенты не должны посылать пакеты CREATE_FAST старым серверам Tor.]
Ели OR видит цепочку, созданную с помощью CREATE_FAST, он может быть уверен, что это первый переход в цепочке. OR-ы должны отклонять попытки создать потоки с помощью RELAY_BEGIN, покидающие цепочку после первого перехода: разрешение Tor'у быть использованному как однопереходный прокси делает выходной узел более привлекательной целью для компрометации.
5.2. Установка ключей цепочки.
После того как ключевое соглашение между OP и OR завершено, оба они могут вычислить gxy как в обычном DH. Перед вычислением gxy и клиент и сервер обязаны проверить, что полученные значения gx и gy не вырожденные; т.е. они должны быть строго больше единицы и строго меньше p-1, где "p" это DH модуль. Приложения не должны завершать ключевое соглашение с вырожденными ключами. Приложения не должны отвергать другие "слабые" значения gx.
(Отбрасывание вырожденных ключей критично для безопасности; если плохие ключи не отвергать, атакующий может подменить gy в пакете CREATED от сервера на 1 или 0, и с известным gxy выдать себя за сервер. Отбрасывание других ключей делает возможными атаки, позволяющие узнать биты секретного ключа.)
(Основные приложения Tor версий 0.1.1.x-alpha отвергали все значения gx меньше чем 224 и больше чем p-224, или имеющие больше чем 1024-16 одинаковых бит. Это было бесполезно и мы от этого отказались.)
Если CREATE или EXTEND используются для расширения цепочки, клиетн и сервер основывают свой ключевой материал на K0=gxy, представленном как "big-endian" беззнаковое целое.
Если используется CREATE_FAST, клиент и сервер основывают свой ключевой материал на K0=X|Y.
Из основного ключевого материала K0 они вычисляют KEY_LEN*2+HASH_LEN*3 байт производного ключа:
K = H(K0 | [00]) | H(K0 | [01]) | H(K0 | [02]) | ...
Первые HASH_LEN байт K образуют KH; следующие HASH_LEN образуют прямой хеш Df; следующие HASH_LEN 41-60 образуют обратный хэш Db; следующие KEY_LEN 61-76 образуют Kf, и последние KEY_LEN образуют Kb. Лишние байты K отбрасываются.
KH используется в ключевом соглашении для демонстрации знания вычисленного общего ключа. Df используется в хэше проверки целостности потока данных, передаваемых от OP к OR. Db используется в хэше проверки целостности потока данных, передаваемых от OR к OP. Kf используется для шифрования потока данных, идущих от OP к OR, и Kb используется для шифрования потока данных, идущих от OR к OP.
5.3. Создание цепочки.
При создании цепочки в сети, создатель цепочки (OP) выполняет следующие шаги:
- Выбирает в качестве последнего узла цепочки такой "луковичный маршрутизатор" OR, (R_N), чтобы его выходная политика включала в себя хотя бы один ожидающий обработки поток, которому необходима цепочка (если такой есть).
- Выбирает цепочку из (N-1) маршрутизаторов OR (R_1...R_N-1) для составления пути таким образом, чтобы ни один маршрутизатор не входил в путь дважды.
- Если соединение с первым маршрутизатором в цепочке еще не установлено, открывает новое соединение к этому маршрутизатору.
- Выбирает circID не используемое в данный момент для соединения с первым маршрутизатором в цепочке; посылает пакет CREATE через это соединение, чтобы он был получен первым маршрутизатором OR.
- Ждет, пока не будет получен пакет CREATED; заканчивает согласование ключей и извлекает прямой ключ Kf_1 и обратный ключ Kb_1.
- Для каждого последующего маршрутизатора R (от R_2 до R_N), выполняет расширение цепочки на этот маршрутизатор.
- 1 Создает "кожицу лука", зашифровая ее открытым ключом "onion key" маршрутизатора R_M.
- 2 Посылает эту "кожицу лука" в качестве данных пакета EXTEND по цепочке (См. секцию 5).
- 3. Когда получен пакет EXTENDED, проверяет KH, вычисляет общие ключи.
Когда OR получает пакет EXTEND, он посылает пакет CREATE следующему OR, включив в него в качестве данных "кожицу лука". Инициирующий OR выбирает значение circID, еще не использующееся в соединении между двумя OR. (Но см. секцию 5.1. выше, относительно выбора circIDs основанного на алфавитном порядке "псевдонимов".)
Если OR получает пакет CREATE со значением circID, которое уже используется в данном соединении в построении цепочки, он отбрасывает пакет. В противном случае, после получения пакета CREATE, он завершает цепочку ключевого согласования DH и отвечает пакетом CREATED. После получения пакета CREATED, OR упаковывает его данные в пакет EXTENDED (см. секцию 5), и отправляет этот пакет по цепочке. После получения пакета EXTENDED, OP может извлечь gy.
(В целях оптимизации, OR приложения могут откладывать обработку "кожиц лука" на некоторое время, но так чтобы сетевые задержки не стали очень большими.)
5.4. Разрушение цепочки.
Цепочка разрушается если в ней произошла невосстановимая ошибка, или по таймауту после закрытия всех потоков в ней. Цепочка может быть разрушена сразу вся или переход за переходом.
Для того чтобы уничтожить цепочку полностью, OR или OP посылает пакет DESTROY смежным узлам цепочки, используя соответствующий circID для данного направления.
После получения пакета DESTROY, OR освобождает ресурсы, связанные с соответствующей цепочкой. Если он не является концом цепочки, он посылает пакет DESTROY для этой цепочки следующему OR в цепочке. Если узел является концом цепочки, он обрывает все связанные с ней соединения (см. секцию 6.1).
После обработки пакета DESTROY, OR игнорирует все пакеты с данными или пакеты разрушения для соответствующей цепочки.
Для разрушения части цепочки, OP может послать пакет RELAY_TRUNCATE для заданного OR (с нулевым ID потока). Этот OR посылает пакет DESTROY следующему узлу в цепочке, и отвечает OP пакетом RELAY_TRUNCATED.
Если во время соединения произошла невосстановимая ошибка в цепочке, узлы по обе стороны от соединения должны действовать следующим образом: узел, расположенный ближе к OP, должен послать пакет RELAY_TRUNCATED для OP, узел расположенный дальше от OP, должен послать пакет DESTROY далее по цепочке.
Данные пакета RELAY_TRUNCATED или DESTROY содержат единственный байт, описывающий почему цепочка будет закрыта или обрезана. Если пакет TRUNCATED или DESTROY посылается из-за получения другого пакета TRUNCATED or DESTROY, код ошибки повторяет полученный. Инициатор цепочки всегда устанавливает код ошибки в "0" во избежание утечки информации о причине.
Коды ошибок:
1 — PROTOCOL (Нарушение протокола Tor.)
2 — INTERNAL (Внутренняя ошибка.)
3 — REQUESTED (Клиент прислал команду TRUNCATE.)
4 — HIBERNATING (Нет текущих операций; попытка разгрузить канал.)
5 — RESOURCELIMIT (Нехватка памяти, портов, или идентификаторов цепочек.)
6 — CONNECTFAILED (Невозможно подключиться к серверу.)
7 — OR_IDENTITY (Подключились к серверу, но его OR идентификатор не тот, что ожидается.)
8 — OR_CONN_CLOSED (Соединение с OR, используемое для этой цепочки, пропало.)
9 — FINISHED (Цепочка закончила функционирование потому что скомпрометирована или устарела.)
10 — TIMEOUT (Построение цепочки заняло слишком много времени.)
11 — DESTROYED (Цепочка уничтожена без команды клиента TRUNCATE.)
12 — NOSUCHSERVICE (Запрос от неизвестного скрытого сервиса.)
[Версии Tor'а до 0.1.0.11 не сообщают причин; приложения должны принимать пустые пакеты TRUNCATED и DESTROY.]
5.5. Маршрутизация пересылаемых пакетов с данными.
Когда OR получает пакет RELAY, он проверяет circID пакета и определяет, есть ли соответствующее этой цепочке подключение. Если нет OR отбрасывает пакет RELAY.
Иначе, если OR не является краевым узлом цепочки со стороны OP (т.е. является выходным или промежуточным узлом), он рас/зашифровывает данные пакета симметричным шифром следующим образом:
Использовать ключ Kf; расшифровать.
'Backward' пакет с данными (направление, противоположное запросу CREATE):
Использовать ключ Kb; зашифровать.
Примечание: в режиме счетчика шифрование и расшифрование представляют собой одну и ту же операцию.
После этого OR пытается распознать пакет, исследуя его содержимое как описано в секции 6.1 ниже. Если OR распознал пакет, он обрабатывает его содержимое. В противном случае он пересылает расшифрованный пакет дальше по цепочке, если эта цепочка продолжается. Если OR, являющийся концом цепочки, встретил нераспознанный пакет, значит произошла ошибка: OR посылает пакет DESTROY и разрушает цепочку.
Когда пакет RELAY поступает на OP, OP расшифровывает данные пакета потоковым шифром следующим образом:
Дополнительную информацию смотри в секции 6 ниже.
6. Управление связью и потоком для приложения.
6.1. Пакеты с данными.
В пределах цепочки OP и узел выхода используют содержимое пакетов RELAY для организации "туннеля" для команд и TCP соединений (потоков) сквозь цепочку. Команды могут быть инициированы любой стороной, потоки инициируются OP.
Данные каждого незашифрованного пакета RELAY состоят из:
'Распознано' [2 байта]
StreamID [2 байта]
Дайджест [4 байта]
Длина [2 байта]
Данные [CELL_LEN-14 байт]
Команды RELAY:
2 — RELAY_DATA [forward или backward]
3 — RELAY_END [forward или backward]
4 — RELAY_CONNECTED [backward]
5 — RELAY_SENDME [forward или backward] [возможно контроль]
6 — RELAY_EXTEND [forward] [контроль]
7 — RELAY_EXTENDED [backward] [контроль]
8 — RELAY_TRUNCATE [forward] [контроль]
9 — RELAY_TRUNCATED [backward] [контроль]
10 — RELAY_DROP [forward или backward] [контроль]
11 — RELAY_RESOLVE [forward]
12 — RELAY_RESOLVED [backward]
13 — RELAY_BEGIN_DIR [forward]
Команды, помеченные как "forward" должны посылаться только организатором цепочки. Команды, помеченные как "backward" должны посылаться только другими узлами. Команды, помеченные обоими направлениями, могут быть посланы как организатором, так и другими узлами цепочки.
Поле 'Распознано' во всех незашифрованых данных всегда установлено в ноль; поле 'Дайджест' вычисляется как первые четыре байта текущего хэша от всех байтов, которые были предназначены для этого перехода цепочки или порождены этим переходом цепочки, начиная от Df или Db соответственно (рассмотрено в секции 5.2 выше), и включая данные этого пакета RELAY (взятые с полем "Дайджест" установленным в "0").
Когда поле 'распознано' пакета RELAY нулевое, и дайджест правильный, пакет считается распознанным после расшифровки (см. секцию 5.5 выше).
Дайджест не включает никаких байтов пакета RELAY, которые не начинаются или не заканчиваются на этом перегоне цепочки. То есть не включаются данные, предназначенные для дальнейшей пересылки. Таким образом, если "распознано" нулевое, но дайджест не соответствует, текущий дайджест узла не обновляется, и пакет пересылается дальше.
Все пакеты RELAY имеющие отношение к одному или тому же туннелируемому потоку имеют один и тот же ID потока. Эти идентификаторы выбираются OP произвольным образом. Пакеты RELAY, котрые влияют не на отдельный поток, а на саму цепочку, используют нулевой streamID — они помечены в таблице выше как пакеты типа "контроль". (Пакет SENDME помечен как "возможно контроль" потому что он может обрабатываться как с использованием streamID, так и независимо от него — смотри Секцию 7.)
Поле "Длина" пакета RELAY содержит число байт в поле данных пакета, которые действительно используются. Остаток поля данных заполняется нулями.
Если пакет RELAY распознан, но "команда" не понята, пакет должен быть отброшен и проигнорирован. Хотя его содержимое учитывается при вычислении дайджеста.
[Версии Tor до 0.1.1.10 закрывали цепочку, если получали неизвестную команду RELAY. Возможно новый вариант будет более совместим с будущими версиями. -RD]
6.2. Открытие потоков и передача данных.
Для открытия нового анонимного TCP соединения OP выбирает открытую цепочку выход которой может соединиться с адресом назначения, выбирает произвольный StreamID, не используемый в этой цепочке, и создает пакет RELAY_BEGIN в данных которого кодирует адрес и порт хоста назначения в следующем формате:
где ADDRESS должен быть DNS именем хоста или адресом IPv4 в формате из четырех чисел, разделенных точками, или адрес IPv6 в квадратных скобках; и PORT – десятичное число между 1 и 65535 включительно.
[Что это за [00] в конце? -NM]
[Так проще работать с данными, как с ASCIIZ строкой -RD]
После получения этого пакета, узел выхода вычисляет адрес, если это необходимо, и открывает новое TCP соединение с указанным портом. Если адрес не может быть вычислен или соединение не может быть установлено, узел выхода отвечает пакетом REPLAY_END. (См. 6.4 ниже.)
В противном случае узел выхода отвечает пакетом RELAY_CONNECTED, данные которого находятся в одном из следующих форматов:
Число секунд (TTL) в течение которых этот адрес может быть закэширован [4 байта]
или
Тип адреса (6)[1 байт]
Адрес IPv6 с которым установлено соединение [16 байт]
Число секунд (TTL) в течение которых этот адрес может быть закэширован [4 байта]
[XXXX Версии Tor'а до 0.1.1.6 игнорируют и не генерируют поле TTL. На текущий момент нет версий Tor генерирующих IPv6 формат.
Сервера Tor до 0.1.2.0 заполняют поле TTL фиксированным значением. Поздние устанавливают в поле TTL последнее значение, полученное от DNS сервера, и очищают свои собственные кэши после фиксированного интервала. Это предотвращает некоторые атаки.]
OP ждет пакет RELAY_CONNECTED перед тем как послать какие либо данные. После того как соединение установлено, OP и узел выхода упаковывают поток данных в пакеты RELAY_DATA, и при получении таких пакетов перенаправляют их содержимое в соответствующий поток TCP.
Пакеты RELAY_DATA посланные для нераспознанных потоков отбрасываются.
Пакеты RELAY_DROP просто дальнодействующие заглушки; после получения такого пекета OR или OP должны отбросить его.
6.2.1. Открытие корневого потока.
Если Tor сервер является корневым, он должен отвечать на пакет RELAY_BEGIN_DIR так, как будто бы получил пакет BEGIN с запросом на соединение с его корневым портом. Пакет RELAY_BEGIN_DIR игнорирует выходную политику, так как этот поток является локальным для процесса Tor.
Если корневой сервис не запущен на Tor сервере, он должен ответить пакетом REASON_NOTDIRECTORY RELAY_END.
Клиенты должны заполнять данные пакетов RELAY_BEGIN_DIR одними нулями, и серверы должны игнорировать эти данные.
[RELAY_BEGIN_DIR не поддерживаются в версиях Tor до 0.1.2.2-alpha; клиенты не должны посылать их маршрутизаторам, на которых запущены ранние версии Tor.]
6.3. Закрытие потоков.
Когда анонимное TCP соединение закрыто, или крайний узел получает ошибку в любом из потоков, он посылает пакет 'RELAY_END' в цепочку (если возможно) и немедленно закрывает все TCP соединения. Если крайний узел получает пакет 'RELAY_END' для любого из потоков, он закрывает все TCP соединения полностью, и не посылает больше ничего по цепочке для данного потока.
Данные пакета RELAY_END начинаются с одного байта 'причина', который описывает, почему поток закрывается, плюс опциональные данные (зависящие от причины.) Используются следующие значения:
2 — REASON_RESOLVEFAILED (не найдено имя хоста)
3 — REASON_CONNECTREFUSED (удаленныйхост отказал в подключении) [*]
4 — REASON_EXITPOLICY (OR отказал в подключении к хосту или порту)
5 — REASON_DESTROY (Разрушена цепочка)
6 — REASON_DONE (Анонимное TCP соединение закрыто)
7 — REASON_TIMEOUT (Закончилось время ожидания соединения, или закончилось время ожидания OR)
8 — (незанято) [**]
9 — REASON_HIBERNATING (OR временно не работает)
10 — REASON_INTERNAL (Внутренняя ошибка OR)
11 — REASON_RESOURCELIMIT (OR не имеет ресурсов для выполнения запроса)
12 — REASON_CONNRESET(Соединение неожиданно сбросилось)
13 — REASON_TORPROTOCOL (Посылается, когда соединение закрывается из-за нарушения протокола Tor.)
14 — REASON_NOTDIRECTORY(Клиент послал RELAY_BEGIN_DIR серверу, не являющемуся корневым.)
(Для REASON_EXITPOLICY, опциональными данными являются 4-байтовый IPv4 адрес или 16-байтовый IPv6 адрес; другие причины дополнительных данных не имеют. Начиная с версии 0.1.1.6, тело также содержит 4-байтовый TTL.)
OPы и ORы должны принимать причины, не указанные в списке, потому что будущие версии Tor могут сообщать более конкретные причины.
[*] Старые версии Tor также посылают эту причину при сбросе соединения.
[**] Из-за ошибки в версии Tor до 0095, причина 8 не должна указываться, пока устаревшие версии полностью не выйдут из обращения.
[Остальная часть секции описывает неосуществленные функциональные возможности.]
Поскольку TCP соединения могут быть "полуоткрыты", мы вводим эквивалент TCP протокола FIN/FIN-ACK/ACK для закрытия потоков.
Выходящее соединение может иметь TCP поток в одном из трех состояний: 'OPEN', 'DONE_PACKAGING', и 'DONE_DELIVERING'. В целях моделирования мы рассматриваем 'CLOSED' как четвертое состояние, хотя соединения в этом состоянии фактически не отслеживаются "луковичными" маршрутизаторами.
Поток начинает с состояния 'OPEN'. После получения 'FIN' от соответствующего TCP соединения, краевой узел посылает пакет 'RELAY_FIN' по цепочке и изменяет состояние потока на 'DONE_PACKAGING'. При получении пакета 'RELAY_FIN', краевой узел посылает 'FIN' соответствующему TCP соединению (например вызывая завершение(SHUT_WR)) и изменяет состояние потока на 'DONE_DELIVERING'.
Если поток уже находящийся в состоянии 'DONE_DELIVERING' получает 'FIN', он также посылает 'RELAY_FIN' по цепочке, и изменяет свое состояние на 'CLOSED'. Когда поток уже находящийся в состоянии 'DONE_PACKAGING' получает пакет 'RELAY_FIN', он посылает 'FIN' и изменяет свое состояние на 'CLOSED'.
Если краевой узел обнаруживает ошибку в любом из потоков, он посылает пакет 'RELAY_END' (если возможно) и немедленно закрывает этот поток.
6.4. Удаленный поиск имени хоста.
Для нахождения адреса, асоциированного с именем хоста, OP посылает пакет RELAY_RESOLVE, содержащий имя хоста, которое должно быть распознано. (Для обратного просмотра, OP посылает пакет RELAY_RESOLVE содержащий in-addr.arpa адрес.) OR отвечает пакетом RELAY_RESOLVED, содержащим байт статуса и любое число ответов. Каждый ответ представлен в следующей форме:
Длина (1 байт)
Значение (переменной длины)
TTL (4 байта)
"Длина" это длина поля "значение".
"Тип" один из:
0x04 — IPv4 адрес
0x06 — IPv6 адрес
0xF0 — Ошибка, транзитная
0xF1 — Ошибка, нетранзитная
Если любой ответ имеет тип "Ошибка", никакие другие ответы не могут быть получены.
Пакет RELAY_RESOLVE должен иметь ненулевой, отличный от испльзуемых streamID; соответствующий пакет RELAY_RESOLVED должен использовать тот же самый streamID. В реальности OR не создает никаких потоков в процессе распознавания имени.
7. Управление потоком данных.
7.1. Link throttling
Each node should do appropriate bandwidth throttling to keep its
user happy.
Communicants rely on TCP's default flow control to push back when they stop reading.
7.2. Заполнение канала связи (покрывающий трафик).
Заполнение канала связи может быть достигнуто посыланием пакетов PADDING по соединению; пакеты типа "DROP" могут быть использованы для удаленного заполнения.
В настоящее время узлы не нуждаются ни в каком типе заполнения или фиктивного трафика. Так как заполение не защищает от мощных атак и слишком повышает требования к полосе пропускания узлов, мы планируем не использовать заполнение, пока его плюсы и минусы не будут лучше изучены.
7.3. Управление передачей данных на уровне цепочки.
Для управления использованием полосы пропускания цепочки, каждый OR отслеживает два "окна", показывающих, сколько пакетов RELAY_DATA он может создать (упаковать для отправки), и как много пакетов RELAY_DATA ожидают обработки (принято для локальных потоков). Эти ограничения не применяются для пакетов, которые OR принимает от одного хоста и передает другому.
Значение каждого "окна" изначально устанавливается в 1000 пакетов данных для обоих направлений (пакеты, не содержащие данных, не влияют на "окно"). Когда OR хочет отправить больше пакетов, он посылает пакет RELAY_SENDME в направлении OP, с нулевым StreamID. Когда OR получает пакет RELAY_SENDME c с нулевым streamID, он увеличивает свое окно упаковки.
Каждый из этих пакетов увеличивает соответствующее окно на 100.
OP действует аналогично, за исключением того, что он должен отслеживать окно упаковки и окно доставки для кадого OR в цепочке.
OR или OP посылает пакеты для увеличения своего окна доставки, когда значение соответствующего окна падает ниже определенного уровня (900).
Как только значение окна упаковки достигает "0", OR или OP прекращают чтение из соответствующего TCP соединения для всехпотоков в соотетствующей цепочке, и не посылают больше пакетов RELAY_DATA до тех пор пока не получат пакет RELAY_SENDME.
[этот материал плохо сформулирован; копия в секции дизайна Tor'а -RD]
7.4. Управление передачей данных на уровне потоков.
Краевой узел использует пакеты RELAY_SENDME для управления сквозным потоком данных для отдельных соединений в цепочке. Подобно тому как при управлении на уровне цепочки краевые узлы начинают с окном пакетов (500) на поток и увеличивают окно на фиксированное значение (50) после получения пакета RELAY_SENDME. Краевые узлы инициируют пакеты RELAY_SENDME при выполнении одновременно двух условий: a) окно <= 450, и b) менее чем 10 пакетов ожидают отправки.
A.1. Различия между спецификацией и приложениями.
Текущая спецификация требует. чтобы все ORы имели IPv4 адреса, но позволяет серверам выходить на IPv6 адреса, распознавать их, и объявлять IPv6 адреса в их политиках выхода. Текущее исполнение вообще не имеет никакой поддержки адресов IPv6.