1. はじめに
前回の記事では、SWUpdate を用いた A/B アップデートについて解説しました。
A/B アップデートは、更新失敗時でもシステムを起動可能な状態に保つための、組み込み機器において非常に重要な手法です。
一方で、ソフトウェア更新を考える際には、
- その更新は 安全に適用できるか
- そもそも 取得している更新は正しいものか
という2つの異なる視点があります。
SWUpdate は署名検証などのセキュリティ機能を備えており、更新イメージの真正性を確認できます。
しかし、更新の配布経路やメタデータ管理まで含めた脅威モデルを考えると、更新の取得段階における一部の攻撃シナリオは SWUpdate 単体ではカバーしきれない場合があります。
本記事では、この課題に対して**TUF™ (The Update Framework™)**と、その軽量クライアントである swutuf を組み合わせる構成を紹介します。
2. TUF 導入の目的
TUFは、更新に関する信頼判断に必要な情報を署名付きメタデータとして配布し、クライアント側で検証するための枠組みです。具体的には、クライアントが「どの更新を信頼すべきか」および「どの更新が最新であるか」を、メタデータの署名・整合性・有効期限・バージョン情報に基づいて判定できるようにします。
TUF を導入することで、
- 古い更新を掴まされ続ける
- 最新ではない更新へ誘導される
- 鍵侵害後に正しく署名された不正更新を受け取る
といった更新取得段階における攻撃に対する耐性を向上させることができます。
SWUpdate が担う「更新の適用」に対し、TUF は「更新の選別および取得(取得物の検証を含む)」を担います。両者は機能分担の観点で補完関係にあります。
2.1 関連発表について
SWUpdate と TUF を組み合わせた安全なソフトウェア更新については、以下の発表で詳しく解説しています。
また、TUF に関しては公開情報が体系的に整備されており、リファレンス実装(python-tuf1)を参照することで仕様との対応関係を確認しやすくなっています。
3. TUF のメタデータと役割
TUF のメタデータは、基本的に「署名対象(signed)」+「署名(signatures)」の2層構造です。
3.1 すべてのメタデータは同じ構造
TUF のメタデータは共通して同一のフォーマットを持ちます。以降では、このフォーマットとロールごとの差分に着目して説明します。
{
"signatures": [
{ "keyid": "...", "sig": "..." }
],
"signed": {
"_type": "timestamp | snapshot | targets | root",
"expires": "2026-12-12T17:11:36Z",
"version": 1,
"spec_version": "1.0.0",
"...": "(roleごとの中身)"
}
}
クライアント側での検証は、概ね次の 3 点で構成されます。
-
signatures[]のkeyidに対応する公開鍵でsignedを検証 -
threshold(必要署名数)を満たしているか確認 -
signed.expiresが期限切れでないか確認
3.2 ロールごとの signed 部(参照関係)
ロールごとの差分はsignedの内容に現れます。ここを追うことで、メタデータ間の参照関係(どの情報がどこに束縛されるか)を整理できます。
3.2.1 Timestamp(timestamp.json)
※ 以下は構造理解のためTOML 風に記載しています。
[signed]
_type = "timestamp"
expires = "2025-12-17T10:16:36Z",
[signed.meta."snapshot.json"]
version = 1 # snapshot.json の version
3.2.2 Snapshot(snapshot.json)
snapshot は、トップレベル targets と delegated targets の version (+任意で hashes/length)を保持します。
[signed]
_type = "snapshot"
expires = "2025-12-17T10:16:36Z"
[signed.meta]
"targets.json" = { version = 1 }
"qemu-amd64.json" = { version = 1 }
3.2.3 Targets(targets.json)
targets は、配布対象の target file について length と hashesを保持します。
[signed]
_type = "targets"
expires = "2025-12-17T10:16:36Z"
[signed.targets."cip-core-qemu-amd64_update.swu"]
hashes.sha256 = "d8006ed3..."
length = 31
3.2.4 Root(root.json)
root は、各ロールの keyids と threshold を定義し、keys に公開鍵情報(key object)を保持します。
[signed]
_type = "root"
expires = "2026-12-12T17:11:36Z"
[signed.roles.root]
keyids = ["5ba1c1e5...", "1474e12a..."]
threshold = 2
[signed.roles.timestamp]
keyids = ["8a1c4a3a..."]
threshold = 1
[signed.roles.snapshot]
keyids = ["59a4df8a..."]
threshold = 1
[signed.roles.targets]
keyids = ["65171251..."]
threshold = 1
# keys[KEYID] は仕様上: { keytype, scheme, keyval{public} }
[signed.keys."8a1c4a3a..."]
keytype = "ed25519"
scheme = "ed25519"
keyval.public = "82ccf6ac..."
3.3 KeyID による対応関係 (root → 各メタデータ)
例として、
- root.json の
signed.roles.timestamp.keyids[0] = 8a1c4a3a... - timestamp.json の
signatures[0].keyid = 8a1c4a3a...
が一致します。同様に Snapshot / Targets も一致します。
すなわち、root が「どの鍵を各ロールの信頼鍵として採用するか」を定義し、各メタデータは対応ロールの鍵により signed 部分へ署名する、という構造になります。
3.4 検証フロー (概要)
クライアントは更新確認時に、概ね次の順で検証を進めます。
Timestamp -> Snapshot -> Targets -> Target files(Artifacts)
(signed) (signed) (signed) (files)
- Timestamp の
signed.meta["snapshot.json"]に基づき snapshot の hash/length/version を取得する - Snapshot の
signed.meta["targets.json"]に基づき targets メタデータの version(+任意で hashes/length)を取得する - Targets の
signed.targets[*]に基づき target file の hashes/length を取得する - target file を取得し、hashes(および length 制約)を検証する
※ Delegated Targets (委譲ロール) は targets.json から追加定義できます。
4. swutuf とは
swutuf は、TUF (The Update Framework) を用いて更新対象(target files)を取得・検証するための、組み込み機器向け軽量クライアントです。
TUF によるメタデータ検証を行い、信頼できる更新ファイルのみを取得します。取得したファイルは通常のファイルとして扱うことも可能です。また SWUpdate と連携する構成では、Unix Domain Socket (UDS) を介して SWUpdate へストリーミング転送することができます。
組み込み機器での利用を前提として、go-tuf を利用しつつも単一バイナリとして配布・利用可能な構成とし、実装の簡素化および依存関係の最小化を重視しています。
swutuf は、CIP™ プロジェクトの Software Update Working Groupにおける検討・検証の一環として開発・利用されており、現在は実験的な実装や議論を進める場としてcip-playgroundリポジトリ2に配置されています。
4.1 特徴
- TUF を用いたファイルのダウンロードおよび検証
- SWUpdate との連携 (UDS 経由、オプション)
- 組み込み環境を想定した軽量な実装
4.2 構成 (簡略)
SERVER (:8011)
+--------------------------------------------+
| +---------------+ +---------------+ |
| | /metadata | | /artifacts | |
| +-------+-------+ +-------+-------+ |
+----------|---------------------|-----------+
| +-----------------+
| |
+----------|---|-----------------------------+
| +-------v---v---+ +---------------+ |
| | swutuf +-----> SWUpdate | |
| +---------------+ +---------------+ |
+--------------------------------------------+
DEVICE
- サーバは TUF メタデータと更新アーティファクトを提供
- swutuf が検証・取得を担当
- SWUpdate が更新を適用
5. QEMU を用いたデモ
本デモでは、CIP のリポジトリである isar-cip-core をベースにした環境を利用します。
isar-cip-core は、CIP Core Generic Profile および CIP SLTS カーネルの Debian® パッケージセットを用いて、仮想ターゲットおよび物理ターゲット向けの ブータブルイメージを生成するためのプロジェクトです。
本記事で使用するリポジトリには、
- swutuf を組み込むための Isar レシピ
- デモ用のcompose.yaml
があらかじめ用意されています。
今回は、オープンソースの CPU エミュレータである QEMU™ を利用し、QEMU 向けイメージを作成してデモンストレーションを行います。
なお、本デモ環境における TUF メタデータの生成 には、RSTUF (Repository Service for TUF) 3 を利用しています。RSTUF は TUF リポジトリのメタデータ管理および署名処理をサービスとして提供する実装であり、本構成では更新アーティファクト登録時に TUF メタデータを自動生成する役割を担います。
本記事のデモは、公開リポジトリを取得することで再現可能です。まず以下を実行してください。
git clone https://github.com/llbxg/sandbox-swutuf.git
cd sandbox-swutuf
デモ用の just レシピや QEMU イメージ、サーバ設定はこのリポジトリ内で管理されています。
ここからは、これまでに説明した構成を QEMU 上で実際に動かして確認します。
デモ環境は手順が整備されており、基本的には just コマンドを順に実行することで再現できます。
5.1 サーバのセットアップ
just server setup
5.2 事前準備
QEMUからホスト上で動作するサーバーへアクセスするため、ホストのIPアドレスを環境変数として設定します。
export HOST_IP="x.x.x.x"
5.3 更新イメージのビルド
まず初めにベースイメージを作成します。
just image build
アップデート先のイメージを作成します。
BUILD_DIR="build2" SW_VERSION="2.0.0" just image build
5.4 更新イメージの登録
更新イメージをサーバーへ登録します。
just upload-v2
5.5 QEMU イメージの起動
just image run
5.6 バージョン確認
cat /etc/sw-versions
# software="1.0.0"
5.7 swutuf による更新実行
QEMU 内で以下を実行します。
swutuf cip-core-qemu-amd64_update.swu
更新完了後自動で再起動します。
5.8 更新の確認
bg_setenv -c
# Environment update was successful.
bg_printenv -p 1 -o revision,ustate
# Using config partition #1
# Values:
# revision: 3
# ustate: 0 (OK)
cat /etc/sw-versions
# software="2.0.0"
6. おわりに
本記事では、SWUpdate を用いた A/B アップデート構成に対し、更新取得段階の信頼性を高める手段として TUF (The Update Framework) およびその軽量クライアントである swutuf を組み合わせる構成について解説しました。
前半では、TUF が提供するメタデータ構造および各ロールの役割を整理し、更新対象がどのように段階的に検証・束縛されるかを示しました。後半では、QEMU 環境上で swutuf と SWUpdate を連携させ、実際に A/B スロットが切り替わる更新フローを確認しました。
本取り組みや、組み込み機器における安全なソフトウェア更新技術に関心をお持ちの方は、ぜひ CIPの Software Update Working Group への参加をご検討ください。
最後までお読みいただき、ありがとうございました。
参考リンク
- デモ環境など(GitHub): llbxg/sandbox-swutuf
- swutuf(コード): cip-playground / swutuf · GitLab
おまけ:全体アーキテクチャ(参考)
本記事で扱った構成を、TUF(RS-TUF) / swutuf / SWUpdate の関係が分かる形で簡略化すると、以下のようになります。
+--------------+
| User |------------------------------------+
+--------------+ 2. Post artifacts infomation |
| |
1. Post artifacts |
| |
+--------------+ +--------------+
| Webserver | | RSTUF API |
| :8080 |←-------------+ | :8008 |
+--------------+ | +--------------+
| | |
| ↓ ↓
| +--------------+ +--------------+
| | Storage | | Broker |
Get metadata +--------------+ +--------------+
Get artifacts ↑ ↑
| | ↓
| | +--------------+
| +------------→| RSTUF Worker |
| Post/Get metadata +--------------+
|
|
+--------------+ /tmp/sockinstctrl +--------------+
| swutuf |---------------------------→| SWUpdate |
+--------------+ Send data via UDS +--------------+
- RSTUF が TUF メタデータを生成
- swutuf がメタデータ検証および更新アーティファクト取得を担当
- SWUpdate が取得済みイメージを適用(A/B 更新)
- The Update FrameworkおよびTUFは、米国及びその他の国における The Linux Foundation の商標
- Debian は、Software in the Public Interest, Inc. の登録商標
- QEMU は、Fabrice Bellard の商標
- TUF は、米国及びその他の国における The Linux Foundation の商標
- CIP は、米国及びその他の国における The Linux Foundation の商標