Машинный перевод, не проверенный носителем языка. Эталоном служит английская версия.

Доверие к приёмнику и морская отрасльПравить на GitHubИсточник: docs/OSNMA.md

Проверяющая сторона приёмника для Galileo OSNMA

Проверяющая сторона приёмника для аутентификации навигационных сообщений Galileo Open Service (OSNMA), на чистом Rust. Она читает страницы I/NAV и сообщает для каждого спутника и каждой эпохи, аутентифицированы ли навигационные данные криптографически и почему проверка не прошла.

Консультативный характер. Это результат анализа и мониторинга, а не навигационное оборудование с утверждённым типом. Сообщаемый здесь статус аутентификации не является службой целостности навигации и не должен служить единственным основанием для какого-либо решения по навигации или безопасности.

УВЕДОМЛЕНИЕ: интеллектуальная собственность в интерфейсе Galileo OSNMA#

Kshana не предоставляет никаких прав на интеллектуальную собственность Европейского союза в Galileo OSNMA Signal-in-Space Interface Control Document («права на ICD»). ICD содержит собственное разрешение (приложение E), которое является неисключительным и безвозмездным и имеет собственные условия, область действия и ограничения. Тот, кто использует, создаёт или распространяет программное обеспечение на основе этого модуля, полагается непосредственно на это разрешение и должен сам прочитать и принять его условия. Лицензия Kshana (только AGPL-3.0 или коммерческая лицензия) охватывает лишь собственный код Kshana.

Этот модуль реализует алгоритмы, которые описывают перечисленные ниже документы. Текст ICD не воспроизводится; числовые значения (раскладки полей, перечисления, таблица соответствия MAC) переизложены в собственных обозначениях Kshana с указанием источника в коде. ICD и Receiver Guidelines — © Европейский союз; источник признаётся здесь. Kshana не разработана, не используется, не одобрена и не поддержана Европейским союзом, Европейской комиссией, EUSPA или каким-либо другим органом.

Справочные документы#

Опубликованы Европейским центром обслуживания ГНСС.

Документ Версия Дата
Galileo OSNMA Signal-in-Space ICD («SIS ICD») Выпуск 1,1 октябрь 2023
Galileo OSNMA Receiver Guidelines Выпуск 1,3 январь 2024

Номера разделов и таблиц, приведённые в коде и здесь, относятся к этим версиям.

Что проверяется#

Цепочка доверия от корня до данных:

  1. Открытый ключ (ICD 3.2.2, 6,2). DSM-PKR собирается из блоков, а его путь Меркла проходится до доверенного корня Меркла (SHA-256).
  2. KROOT (ICD 3.2.3, 6,3). DSM-KROOT собирается; его подпись ECDSA (P-256/SHA-256 или P-521/SHA-512) проверяется по открытому ключу, который он называет, над заголовком NMA и параметрами цепочки.
  3. Ключи TESLA (ICD 5,5, 6,4). Каждый ключ в сообщении MACK прослеживается назад до KROOT или до более раннего проверенного ключа с помощью хеш-функции цепочки.
  4. MACSEQ (ICD 4.1.2, 6,6). Гибкие поля Tag-Info сверяются с MACSEQ с помощью ключа, который открывает этот MACK.
  5. Теги (ICD 4,2, 6,7). Каждый тег пересчитывается (HMAC-SHA-256 или CMAC-AES, усечённый до длины тега цепочки) над идентификаторами спутников, временем подкадра, позицией тега, статусом NMA и навигационными данными, которые он охватывает, и сравнивается. Обрабатываются ADKD 0 (эфемериды, часы и статус), ADKD 4 (параметры времени) и ADKD 12 (медленный MAC, ключ публикуется десятью подкадрами позже), с перекрёстной аутентификацией спутников, которые сами не несут OSNMA, и фиктивные теги (COP 0).

Тег, который называет зарезервированное значение или у которого ADKD не соответствует слоту таблицы соответствия MAC, откладывается в сторону (Receiver Guidelines предписывают его отбросить) и сообщается как discarded, а не как отказ.

Страницы проверяются по CRC (CRC-24Q Galileo) прежде всего остального; страница, не прошедшая проверку, отбрасывается и о ней сообщается (bad_crc).

Значения статуса#

Для каждого тега: authenticated, failed с причиной (tag_mismatch, macseq_mismatch, key_chain_mismatch, key_conflict), pending с причиной (no_chain, awaiting_key, no_nav_data, unknown_maclt, unsupported_function, service_not_usable, chain_mismatch) или discarded. Фиктивный тег (COP 0) на уровне тега показывается как authenticated, поскольку он подтверждает ключ, но не охватывает навигационных данных и никогда не засчитывается для спутника.

Для каждого спутника, в форме, которую использует монитор receiver-trust:

  • Authenticated только пока самые новые проверенные данные эфемерид и часов (ADKD 0 или
    1. этого спутника свежи (в пределах 600 с от времени проверяющей стороны по умолчанию) и всё ещё остаются используемыми данными. Проверяются данные, а не IODnav: проверяющая сторона хранит SHA-256 аутентифицированной битовой строки (Word Types 1–5, как их охватывает ADKD 0) и самой новой такой строки, которую передаёт спутник, и они должны быть равны. Скопированный IODnav поверх других слов или изменённые биты Word Type 5 (которые не несут IODnav) читаются как Unavailable, пока новые данные не пройдут аутентификацию. Подкадр, который несёт слова эфемерид, но не полный набор Word Types 1–5 под одним IODnav, так же лишает статуса (каждый подлинный подкадр E1-B несёт все пять); подкадр вовсе без слов эфемерид ничего не говорит. Теги другого спутника (перекрёстная аутентификация) засчитываются тому спутнику, чьи данные они охватывают.
  • Failed, если проверка данных спутника не прошла в пределах памяти об отказе (600 с по умолчанию от времени проверяющей стороны), даже если более поздние данные проходят проверку. Несоответствие относится на счёт спутника, чьи данные охватывает тег, и спутника, передавшего тег, так как виноват мог быть любой из них; несоответствие MACSEQ относится на счёт передающего спутника.
  • Unavailable в остальных случаях, а также для каждого спутника после проверенного сообщения-оповещения, при статусе NMA «не использовать» или зарезервированном статусе и до проверки первого ключа.

Общее значение — Failed, если хоть один спутник не прошёл проверку, Authenticated, если хотя бы один аутентифицирован и ни один не отказал, иначе Unavailable. $PKSOS,<A|F|N>[,<sat>:<A|F|N>...] — соответствующее предложение; kshana::osnma::pksos_sentence строит его.

Время#

Цепочка ключей показывает, что ключ принадлежит данному подкадру, а не то, что подкадр — нынешний. Поэтому проверяющая сторона никогда не берёт «сейчас» с непроверенной страницы. Её время — более позднее из опорного времени, заданного через set_reference_time (--reference-time), и подкадра последнего проверенного ключа TESLA (ключ не может быть известен до своего слота, так что он доказывает, что время не раньше). Память об отказах, окно действительности аутентификации и вытеснение старых данных работают по этому времени. Подкадр с отметкой более чем на три подкадра позже последнего проверенного ключа откладывается в сторону, если его собственный ключ не доказывает его время; ключевые сообщения и сообщения-оповещения в нём всё равно обрабатываются, так как несут собственное доказательство (подпись или корень Меркла).

Часы стоят на месте, когда OSNMA останавливается, поскольку их двигают только ключи. Вызывающая сторона в живом режиме должна вызывать set_reference_time со временем хоста каждый подкадр; тогда аутентификация истекает сама, а poll_clock (также запускаемый с каждым подкадром) возвращает событие KeyStall, как только ни один ключ не проверялся дольше окна аутентификации.

Чего это не даёт — так это свежести. Без опорного времени из источника, который злоумышленник не может сдвинуть, повтор старых подлинных данных под старыми отметками проходит аутентификацию, а отметки страниц хороши лишь настолько, насколько хорош их источник: CLI сообщает об этом (freshness: NOT enforced), когда опорное время не задано. При заданном опорном времени допуск меньше одного подкадра (по умолчанию 29 с; большие значения отклоняются). Опорное время — это один момент: подкадры дальше от него, чем допуск, откладываются и учитываются, поэтому оно подходит для живых или только что записанных данных, а не для длинной записи. Для записи опустите опорное время и читайте результат как «подлинно, не обязательно свежо». Формальное требование даёт Receiver Guidelines.

Отметки страниц потока u-blox берутся из самих неаутентифицированных данных (см. «Вход»), поэтому они тоже не дают защиты свежести.

Цепочки, ключи и оповещения#

Проверенные цепочки хранятся по идентификатору цепочки (CID); теги проверяются по цепочке, которую называет заголовок NMA, а заголовок, называющий непроверенную цепочку, оставляет теги подкадра непроверенными (chain_mismatch). Второй KROOT действующей цепочки добавляет якорь; KROOT другой цепочки хранится рядом, так что повторное объявление старой цепочки после обновления не отменяет новую.

Заголовок NMA не аутентифицирован, поэтому то, что он объявляет, учитывается только при подтверждении: когда более одного спутника передаёт один и тот же заголовок в подкадре или когда его несёт подписанное сообщение проверенного KROOT (заголовок — часть этого сообщения). Эти два случая не равны. Согласие более чем одного спутника защищает от аномалии одного спутника, но не от спуфера: один спуфер может подделать два идентификатора спутников. Это даёт ему лишь отказ в обслуживании и ничего больше, так как подтверждённое «не использовать» очищает аутентифицированные данные каждого спутника: снятие его, поддельное или подлинное, ничего не восстанавливает, пока новые теги не пройдут проверку на подлинных рабочих подкадрах. (До этой очистки поддельное снятие могло вернуть на срок до окна аутентификации данные, уже прошедшие аутентификацию.) Настоящее подтверждение — заголовок, подписанный KROOT, который к тому же снова снимает «не использовать», если несёт рабочий статус. Заголовок одного спутника не меняет ничего, кроме проверки тегов собственного подкадра. KROOT, время применимости которого не позже, чем у цепочки, удерживаемой с тем же идентификатором (повтор более старого), отклоняется (stale); KROOT той же цепочки добавляет якорь.

Статус и значение CPKS подтверждённого заголовка управляют отзывом. При статусе NMA «не использовать» CPKS «цепочка отозвана» убирает цепочку, которую называет заголовок, и с этого момента отклоняет её KROOT; CPKS «открытый ключ отозван» убирает открытый ключ, подписавший эту цепочку, и каждую цепочку, подписанную им. Если заменяющая цепочка для этого идентификатора была принята до подтверждения заголовка, как бы давно это ни было, то отзываемый ключ — это ключ предыдущей цепочки, а не новой; эта запись сохраняется, пока отзыв не выполнен или не подтверждён заголовок другой процедуры, а заголовок отзыва, повторяемый в каждом подкадре, выполняется один раз. Те же значения CPKS при рабочем статусе отмечают переход на заменяющую цепочку и ничего не отзывают. Сам заголовок не аутентифицирован, поэтому поддельный отзыв может только лишить аутентификации, и это окончательно до перезапуска проверяющей стороны. Эта окончательность распространяется и на действующий ключ: поддельный отзыв открытого ключа сбрасывает запись о замещённом ключе и отзывает используемый ключ, и отменить это может только перезапуск.

DSM-PKR с NPKT 4 — это сообщение-оповещение; оно проверяется через дерево Меркла как ключ, а его случайное поле NPK входит в лист. Проверенное оповещение очищает все цепочки и открытые ключи, отклоняет каждый последующий KROOT и открытый ключ и делает каждый спутник Unavailable, как предписывает Receiver Guidelines, до перезапуска проверяющей стороны.

Поле заполнения DSM-KROOT — это хеш подписанного сообщения и подписи. Оно проверяется до подписи, а также указывает, с каким заголовком NMA было построено подписанное сообщение, если заголовок изменился во время сборки сообщения.

Что не проверяется или не задействуется#

  • Свежесть. См. «Время» выше: нужно опорное время извне.
  • Обновление и отзыв. Цепочки и ключи прослеживаются так, как их задействуют потоки опубликованных векторов (обновление, конец цепочки, отзыв цепочки и ключа, новый открытый ключ, новое дерево Меркла, оповещение), а не каждый случай, допускаемый ICD. Отзыв, объявленный неаутентифицированным заголовком, принимается на веру и окончателен на время сеанса. Новое дерево Меркла подхватывается только через корень Меркла, который передан проверяющей стороне.
  • Хеш дерева Меркла. Сообщения DSM-PKR проверяются деревом SHA-256. Дерево SHA3-256, которое ICD допускает в будущем, не поддерживается. Хеш-функцией цепочки может быть SHA-256 или SHA3-256.
  • Списки статусов ключей. Отзыв прослеживается через заголовок NMA и сообщения в потоке; списки отзыва сертификатов не читаются.
  • Охват аутентифицированных данных. ADKD 4 с COP 1, переданный в нечётном подкадре, не может быть аутентифицирован (Word Type 10 может быть передан на один или два подкадра раньше); это стоит только доступности, но никогда не корректности.
  • Работа за постоянное время. Не обеспечивается и не нужна: проверяющая сторона не хранит секретов (только открытые ключи, опубликованные ключи цепочки и принятые данные).
  • Качество сигнала и данных. Страницы проверяются по CRC, а страницы-оповещения отбрасываются, но о сигнале, на котором они пришли, ничего не известно. Восстановление Рида–Соломона слов I/NAV 1–4 не используется: данные должны приниматься напрямую.
  • Это не служба целостности положения. Аутентификация говорит, что данные пришли от системы. Она ничего не говорит о сигнале, на котором они пришли, и о качестве решения.

Входная величина#

  • Простой формат страниц. Одна пара страниц в строке: <svid> <gst_seconds> <60 hex digits>, допускаются комментарии #. gst_seconds — это GST в секундах (week * 604800 + time of week) в начале страницы; шестнадцатеричная запись — это 240 бит чётной и нечётной половин (с хвостами), старший бит первым.
  • CSV тестовых векторов. Раскладка опубликованных векторов (SVID,NumNavBits,NavBitsHEX). Время начала берётся из --start-gst или из датированного имени файла.
  • u-blox UBX-RXM-SFRBX. Необработанный поток байтов UBX (--format ubx, определяется автоматически). Сообщения Galileo E1-B I/NAV собираются в пары страниц (src/osnma/ubx.rs); другие системы, E5b, страницы-оповещения и кадры с неверной контрольной суммой игнорируются. SFRBX не несёт времени, поэтому каждая страница получает отметку по времени внутри собственных данных (типы слов 5 и 0) и принимается, только если последовательные страницы со временем согласуются с числом страниц, принятых между ними; страницы, которые нельзя разместить, отбрасываются, а не угадываются. Это время берётся из самих данных и не даёт защиты свежести (см. «Время» выше). Идентификатор сигнала 0 (E1-C) не несёт данных, поэтому приём его наряду с E1-B безвреден. Случайная пара синхронизации с абсурдной длиной не задерживает модуль чтения. Код кадрирования — третья копия кадрирования UBX в дереве (src/realdata/ubx.rs, приём receiver-trust); она хранится отдельно, чтобы поведение receiver-trust не менялось, и позже эти три можно свести к одному модулю чтения. Раскладка слов сообщения SFRBX взята из публичных описаний и проверена только на синтетических кадрах, а не на живом приёмнике.

Командная строка#

kshana osnma verify <input> [--format pages|vector-csv|ubx] [--start-gst SECONDS]
    [--merkle-root HEX] [--public-key ID:TYPE:HEX]
    [--reference-time SECONDS [--max-time-error SECONDS]] [--epochs] [--json]

Текстовый вывод даёт статус для каждого спутника и, с --epochs, для каждого тега и эпохи; --json даёт то же со списком тегов. Код завершения 3 означает, что проверка данных какого-то спутника не прошла, 2 — ошибку использования или входных данных. Консультативное сообщение, приведённое выше, печатается первым на любом пути, включая ошибки и справку по использованию.

Из Python и WebAssembly#

kshana.osnma_verify(data, format="", start_gst=None, merkle_root_hex="", public_keys=None, reference_time=None, max_time_error=None) (Python) и osnma_verify(input_base64, format, start_gst, merkle_root_hex, public_keys, reference_time, max_time_error) (WebAssembly) запускают ту же проверяющую сторону на байтах в памяти и возвращают JSON, который печатает kshana osnma verify --json, включая консультативное сообщение. Сервер MCP её не содержит: вход — длинный необработанный поток приёмника, который лучше обслуживают командная строка и Python, а инструмент агента добавил бы большую поверхность для входных данных без пользы. Действуют то же консультативное сообщение и те же ограничения; статус не является службой целостности навигации.

Тесты и тестовые данные#

Тестовые данные синтетические и создаются собственным кодом и тестами Kshana. В этом репозитории не хранятся ни векторы ICD или Guidelines, ни разобранные примеры, ни ключевой материал. Тесты по умолчанию (tests/osnma_synthetic.rs и модульные тесты) строят цепочку, страницы и теги по уравнениям и проверяют согласованность и то, что намеренное повреждение обнаруживается: перевёрнутый навигационный бит, перевёрнутый бит ключа, повторённые теги, неверные отметки времени, время вне опорного, отсутствующие данные и атаки, найденные при разборе (поддельный подкадр из далёкого будущего, который не должен стирать отказ, аутентификация, которая должна истекать и следовать за IODnav, оповещение, статус NMA «не использовать», фиктивные теги, несоответствие MACSEQ, конфликтующие ключи, отзыв и обновление цепочки, неверное заполнение или подпись), причём CMAC-AES, цепочка SHA3-256 и подпись P-521 проходят через проверяющую сторону. Поскольку эти данные получены из нашего собственного прочтения ICD, они показывают самосогласованность, а не соответствие.

Примитивы проверяются по опубликованным тестам с известными ответами, ни один из которых не взят из ICD: RFC 4231 (HMAC-SHA-256), RFC 4493 (CMAC-AES-128), FIPS 197 (AES), FIPS 202 (SHA3-256) и RFC 6979 (ECDSA P-256). Подписи P-521 проверяются на кодирование и выбор хеша с одноразовым ключом, созданным в тесте; арифметика кривой — это арифметика библиотеки.

Соответствие проверяет tests/osnma_official_vectors.rs, который включается по желанию: он читает копию официального архива тестовых векторов, которую вы загружаете сами, из каталога, названного KSHANA_OSNMA_VECTORS, и ничего не делает, если переменная не задана. Он ничего не хранит и не распространяет, а прогоны тестов по умолчанию не используют сеть. Он даёт проверяющей стороне только корень Меркла и открытые ключи одного дерева, как было бы у приёмника, поэтому цепочки устанавливаются из подписанного DSM-KROOT в потоке и проходит весь путь: Меркл, подпись, цепочка ключей, MACSEQ и теги. Для каждого набора векторов он проверяет нижнюю границу числа аутентифицированных тегов, отсутствие отказавших, какие цепочки и ключи объявляет поток, аутентифицированные теги ADKD 0, 4 и 12, что аутентификация возобновляется после конца цепочки, отзыва цепочки, отзыва ключа и нового открытого ключа, что сообщение-оповещение проверяется и ничего не оставляет аутентифицированным, и что каждый вектор использует только HMAC-SHA-256, цепочку SHA-256, 128-битные ключи, 40-битные теги и записи таблицы соответствия 33 и 34. Другие варианты алгоритмов и параметров ICD поэтому охвачены только тестами с известными ответами и синтетическими тестами, описанными выше.

Зависимости#

Крейты RustCrypto, чистый Rust, MIT OR Apache-2.0, собранные без функций по умолчанию: hmac, cmac, aes, sha3, p256 и p521 (функция ecdsa, используется только для проверки), наряду с sha2, уже имеющимся в дереве. Они собираются для wasm32-unknown-unknown и проходят cargo deny.