Galileo OSNMA 受信機側ベリファイア
Galileo Open Service Navigation Message Authentication(OSNMA)の受信機側ベリファイアで、純粋な Rust で書かれています。I/NAV ページを読み取り、衛星ごと、エポックごとに、航法データが暗号的に認証されているかどうか、また検査が失敗した場合はその理由を報告します。
助言のみ。これは解析・監視の出力であり、型式承認を受けた航法装置ではありません。ここで報告される認証ステータスは、航法インテグリティサービスではなく、航法または安全に関するいかなる判断の唯一の根拠としても使用してはなりません。
告知:Galileo OSNMA インターフェースの知的財産#
Kshana は、Galileo OSNMA Signal-in-Space Interface Control Document(「ICD IPRs」)に関する欧州連合の知的財産に基づく権利を一切付与しません。ICD には独自の許諾(その Annex E)があり、非独占的かつロイヤリティフリーで、独自の条件、範囲、制限があります。このモジュールに基づくソフトウェアを使用、構築、配布する人は、その許諾に直接依拠することになり、その条件を自ら読んで受け入れる必要があります。Kshana のライセンス(AGPL-3.0-only、または商用ライセンス)が対象とするのは、Kshana 自身のコードのみです。
このモジュールは、以下の文書が記述するアルゴリズムを実装しています。ICD の文章は転載していません。数値(フィールドのレイアウト、列挙、MAC ルックアップテーブル)は Kshana 独自の表記で書き直し、出典はコード内に記載しています。ICD と Receiver Guidelines は © 欧州連合であり、出典はここに明記します。Kshana は、欧州連合、欧州委員会、EUSPA、その他のいかなる団体によっても、開発、使用、承認、推奨されていません。
参照文書#
European GNSS Service Centre が公開。
| 文書 | バージョン | 日付 |
|---|---|---|
| Galileo OSNMA Signal-in-Space ICD(「SIS ICD」) | Issue 1.1 | 2023 年10月 |
| Galileo OSNMA Receiver Guidelines | Issue 1.3 | 2024 年1月 |
コード内およびここで引用している節番号と表番号は、これらの版を指します。
検証される内容#
ルートからデータまでの信頼の連鎖:
- 公開鍵(ICD 3.2.2、6.2)。DSM-PKR をブロックから再構成し、その Merkle パスをたどって、信頼された Merkle ルート(SHA-256)まで検証します。
- KROOT(ICD 3.2.3、6.3)。DSM-KROOT を再構成し、その ECDSA 署名(P-256/SHA-256 または P-521/SHA-512)を、署名が指定する公開鍵に対して、NMA ヘッダーとチェーンパラメーターを対象に検査します。
- TESLA 鍵(ICD 5.5、6.4)。MACK メッセージ内の各鍵は、チェーンのハッシュ関数で、KROOT または先に検証済みの鍵までたどられます。
- MACSEQ(ICD 4.1.2、6.6)。可変の Tag-Info フィールドを、その MACK を開く鍵を使って MACSEQ と照合します。
- タグ(ICD 4.2、6.7)。各タグは、衛星の識別子、サブフレーム時刻、タグの位置、NMA ステータス、およびそれが対象とする航法データについて再計算し(HMAC-SHA-256 または CMAC-AES、チェーンのタグ長に切り詰め)、比較されます。ADKD 0(エフェメリス、時計、ステータス)、ADKD 4(タイミングパラメーター)、ADKD 12(スロー MAC。鍵は 十サブフレーム後に公開)に対応しており、それ自体は OSNMA を搬送しない衛星の相互認証と、ダミータグ(COP 0)も扱います。
予約値を指すタグ、または ADKD が MAC ルックアップテーブルのスロットと一致しないタグは、除外され(Receiver Guidelines は破棄するよう定めています)、失敗ではなく discarded として報告されます。
ページは、他の何よりも先に CRC(Galileo CRC-24Q)で検査されます。失敗したページは破棄され、報告されます(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 と表示されますが、航法データを対象とせず、衛星についてはカウントされません。
衛星ごと(受信機信頼性モニターが消費する形式):
Authenticatedとなるのは、最新の検証済みエフェメリスおよび時計データ(ADKD 0 または- )がその衛星のものとして新しく(既定ではベリファイアの時刻から 600 秒以内)、かつ現在使用中のデータである間に限られます。検査の対象は IODnav ではなくデータです。ベリファイアは、認証されたビット列(ADKD 0 が対象とする Word Type 1 から 5)と、その衛星が送る最新の同種のビット列の SHA-256 を保持し、両者は等しくなければなりません。他のワードに複製された IODnav や、改変された Word Type 5 のビット(IODnav を持たない)は、新しいデータが認証されるまで
Unavailableと表示されます。エフェメリスのワードは持つが、一つの IODnav の下で Word Type 1 から 5 の完全なセットを持たないサブフレームは、同様にステータスを失います(本物の E1-B サブフレームはすべて 五つとも持ちます)。エフェメリスのワードが全くないサブフレームは何も示しません。他の衛星のタグ(相互認証)は、そのデータが対象とする衛星のものとしてカウントされます。
- )がその衛星のものとして新しく(既定ではベリファイアの時刻から 600 秒以内)、かつ現在使用中のデータである間に限られます。検査の対象は IODnav ではなくデータです。ベリファイアは、認証されたビット列(ADKD 0 が対象とする Word Type 1 から 5)と、その衛星が送る最新の同種のビット列の SHA-256 を保持し、両者は等しくなければなりません。他のワードに複製された IODnav や、改変された Word Type 5 のビット(IODnav を持たない)は、新しいデータが認証されるまで
Failedとなるのは、その衛星のデータに対する検査が失敗メモリの期間内(既定ではベリファイアの時刻で 600 秒)に失敗した場合で、その後のデータが検証されても同じです。不一致は、タグが対象とするデータの衛星と、そのタグを送信した衛星の両方に計上されます。どちらにも誤りがありうるためです。MACSEQ の不一致は、送信した衛星に計上されます。- それ以外の場合、また、検証済みのアラートメッセージの後のすべての衛星、NMA ステータスが「don't use」または予約済みステータスの場合、最初の鍵が検証されるまでは、
Unavailableです。
全体の値は、いずれかの衛星が失敗した場合は Failed、少なくとも一つが認証され、失敗がない場合は Authenticated、それ以外は Unavailable です。$PKSOS,<A|F|N>[,<sat>:<A|F|N>...] は対応するセンテンスで、kshana::osnma::pksos_sentence がそれを組み立てます。
時刻#
鍵チェーンが示すのは、鍵が特定のサブフレームに属することであり、そのサブフレームが現在のものであることではありません。そのため、ベリファイアは未検証のページから「現在」を取ることはありません。その時刻は、set_reference_time(--reference-time)で与えられた基準時刻と、検証された最新の TESLA 鍵のサブフレームのうち、遅い方です(鍵はそのスロットより前には知りえないため、それ以前ではないことが証明されます)。失敗メモリ、認証の有効期間、古いデータの破棄は、この時刻に基づいて動作します。検証された最新の鍵より 三サブフレームを超えて先のスタンプが付いたサブフレームは、その鍵自体が時刻を証明しない限り除外されます。その中の鍵メッセージとアラートメッセージは、それぞれ独自の証明(署名または Merkle ルート)を持つため、引き続き処理されます。
OSNMA が停止すると、時計は止まります。時計を進めるのは鍵だけだからです。ライブの呼び出し側は、サブフレームごとにホスト時刻を指定して set_reference_time を呼び出す必要があります。すると認証は自然に失効し、poll_clock(これもサブフレームごとに実行)は、認証ウィンドウより長く鍵が検証されなかった時点で KeyStall イベントを返します。
これが与えないのは鮮度です。攻撃者が動かせない情報源からの基準時刻がなければ、古い本物のデータを古いスタンプのまま再生しても認証され、ページのスタンプは、その情報源の信頼性以上のものにはなりません。基準時刻が与えられていない場合、CLI は毎回その旨を表示します(freshness: NOT enforced)。基準時刻がある場合の許容差は 一サブフレーム未満です(既定は 29 秒。これより大きい値は拒否されます)。基準時刻は単一の瞬間です。そこから許容差より離れたサブフレームは除外されてカウントされるため、ライブまたは記録直後のデータに向いており、長い記録には向きません。記録の場合は基準時刻を指定せず、結果を「認証されているが、必ずしも新しいとは限らない」と読んでください。正式な要件は Receiver Guidelines にあります。
u-blox ストリームのページスタンプは、未認証のデータ自体から得られる(入力を参照)ため、これも鮮度の保護にはなりません。
チェーン、鍵、アラート#
検証済みのチェーンはチェーン ID(CID)ごとに保持されます。タグは NMA ヘッダーが指定するチェーンで検査され、検証されていないチェーンを指定するヘッダーがある場合、そのサブフレームのタグは未検査のままになります(chain_mismatch)。有効なチェーンの 二つ目の KROOT はアンカーを追加し、別のチェーンの KROOT は並行して保持されるため、更新後に旧チェーンを再び通知しても、新しいチェーンは取り消されません。
NMA ヘッダーは認証されないため、それが通知する内容は、確認された場合にのみ有効です。確認とは、同じヘッダーをサブフレーム内で複数の衛星が送っている場合、または検証済みの KROOT の署名付きメッセージがそれを含んでいる場合です(ヘッダーはそのメッセージの一部です)。この二つは同等ではありません。複数の衛星が一致することは、単一衛星の異常には備えになりますが、スプーファーには備えになりません。一つのスプーファーが 二つの衛星 ID を偽装できるからです。それによって得られるのはサービス拒否であり、それ以上のものではありません。確認された「don't use」はすべての衛星の認証済みデータを消去するため、それを解除しても(偽造であれ本物であれ)、本物の運用サブフレームの下で新しいタグが検証されるまで、何も回復しません。(その消去の前であれば、偽造された解除によって、認証ウィンドウの間、すでに認証されていたデータが復活する可能性がありました。)本当の確認は KROOT で署名されたヘッダーであり、運用ステータスを持つ場合には「don't use」を再び解除することもできます。単一の衛星のヘッダーは、その衛星自身のサブフレームのタグ検査以外には何も変えません。同じ ID で保持しているチェーンの適用時刻より遅くない適用時刻の KROOT(古いものの再生)は拒否されます(stale)。同じチェーンの KROOT はアンカーを追加します。
確認されたヘッダーのステータスと CPKS の値が、失効処理を決めます。NMA ステータスが「don't use」のとき、CPKS「chain revoked」はヘッダーが指定するチェーンを破棄し、以後その KROOT を拒否します。CPKS「public key revoked」は、そのチェーンに署名した公開鍵と、それで署名されたすべてのチェーンを破棄します。そのチェーン ID の後継チェーンが、ヘッダーの確認より前に(どれほど前であっても)採用されていた場合、失効される鍵は新しいチェーンのものではなく、前のチェーンのものです。この記憶は、失効が実行されるか、別の手順のヘッダーが確認されるまで保持されます。サブフレームごとに繰り返される失効ヘッダーは、一度だけ実行されます。同じ CPKS の値でステータスが operational の場合は、後継への移行を示すだけで、何も失効させません。ヘッダー自体は認証されないため、偽造された失効は認証を奪うことしかできず、ベリファイアを再起動するまで元に戻りません。この不可逆性は使用中の鍵にも及びます。偽造された公開鍵の失効は、置き換えられた鍵の記憶をリセットして使用中の鍵を失効させ、再起動だけがそれを元に戻します。
NPKT 4 の DSM-PKR はアラートメッセージです。鍵と同様に Merkle ツリーを通して検証され、そのランダムな NPK フィールドはリーフの一部です。検証されたアラートは、すべてのチェーンと公開鍵を消去し、以後のすべての KROOT と公開鍵を拒否し、Receiver Guidelines の指示どおり、ベリファイアを再起動するまで、すべての衛星を Unavailable にします。
DSM-KROOT のパディングフィールドは、署名対象のメッセージと署名のハッシュです。署名より先に検査され、メッセージの組み立て中にヘッダーが変わった場合に、署名対象のメッセージがどの NMA ヘッダーで作られたかも示します。
検証されていない、または実行されていない内容#
- 鮮度。上の「時刻」を参照してください。外部からの基準時刻が必要です。
- 更新と失効。チェーンと鍵は、公開されているテストベクターのストリームが実行する範囲(更新、チェーンの終了、チェーンと鍵の失効、新しい公開鍵、新しい Merkle ツリー、アラート)でたどられ、ICD が許すすべてのケースではありません。未認証のヘッダーが通知する失効は、その言葉どおりに受け取られ、セッション中は不可逆です。新しい Merkle ツリーは、ベリファイアに与えられた Merkle ルートを通してのみ取り込まれます。
- Merkle ツリーのハッシュ。DSM-PKR メッセージは SHA-256 ツリーで検査されます。ICD が将来許容する SHA3-256 ツリーには対応していません。チェーンのハッシュ関数は SHA-256 または SHA3-256 にできます。
- 鍵ステータスリスト。失効は、NMA ヘッダーとストリーム内のメッセージを通じてたどられます。証明書失効リストは読み取りません。
- 認証されるデータの範囲。奇数サブフレームで送られる COP 1 の ADKD 4 は認証できません(Word Type 10 は、一つまたは二つ前のサブフレームで送られることがあります)。これで失われるのは可用性だけで、正しさが損なわれることはありません。
- 定数時間での動作。提供しておらず、必要でもありません。ベリファイアは秘密を保持しないためです(公開鍵、公開されたチェーン鍵、受信データのみ)。
- 信号とデータの品質。ページは CRC で検査され、アラートページは破棄されますが、それらが届いた信号については何も分かりません。I/NAV ワード 1 から 4 の Reed-Solomon による復元は使用しません。データは直接受信する必要があります。
- 位置インテグリティサービスではありません。認証が示すのは、データがシステムから来たということです。それが届いた信号や、測位解の品質については何も示しません。
入力#
- プレーンページ形式。一行に一組のページペア:
<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)でスタンプされ、時刻を持つ連続するページが、その間に受信したページ数と一致する場合にのみ受け入れられます。位置を決められないページは破棄され、推測されることはありません。この時刻はデータ自体に由来し、鮮度の保護にはなりません(上の「時刻」を参照)。信号 ID 0(E1-C)はデータを搬送しないため、E1-B と並べて受け入れても害はありません。異常に長い長さを持つ孤立した同期ペアがあっても、リーダーが止まることはありません。フレーミングのコードは、ツリー内にある UBX フレーミングの三つ目の複製です(src/realdata/ubx.rs、受信機信頼性の取り込み部)。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)は、メモリ上のバイト列に対して同じベリファイアを実行し、kshana osnma verify --json が出力する JSON を、助言を含めて返します。MCP サーバーには搭載していません。入力が長い生の受信機ストリームであり、コマンドラインと Python の方が適しているうえ、エージェント用ツールは利点のないまま大きな入力の攻撃面を加えるためです。同じ助言と同じ制限が適用されます。このステータスは航法インテグリティサービスではありません。
テストとテストデータ#
テストデータは合成であり、Kshana 自身のコードとテストで生成されます。ICD や Guidelines のベクター、worked example、鍵素材は、このリポジトリに保存されていません。既定のテスト(tests/osnma_synthetic.rs と単体テスト)は、数式からチェーン、ページ、タグを構築し、整合性と、意図的な破損が検出されることを検査します。反転した航法ビット、反転した鍵ビット、再生されたタグ、誤った時刻スタンプ、基準から外れた時刻、欠落データ、そしてレビューで見つかった攻撃(失敗を消してはならない、偽装された遠い未来のサブフレーム、失効して IODnav に追従しなければならない認証、アラート、NMA ステータス「don't use」、ダミータグ、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 が指すディレクトリから読み取り、変数が未設定の場合は何もしません。何も保存も再配布もせず、既定のテスト実行ではネットワークを使いません。受信機と同じように、ベリファイアには一つのツリーの Merkle ルートと公開鍵だけを与えるため、チェーンはストリーム内の署名付き DSM-KROOT から確立され、Merkle、署名、鍵チェーン、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 を通過します。