署名 OK・照合 FAIL — 証跡が別の成果物を指すとき
目標
エアギャップ環境に入ってきた成果物と一緒に来た証憑3種(CycloneDX SBOM・in-toto Statement・SLSA Provenance)が、本当にその成果物を指しているかを、インターネットなしで突き合わせ、確認したことと確認できなかったことを分けて書いた持ち込み審査報告書を残します。
なぜ重要なのか
持ち込み審査での「署名検証通過」は、思ったよりはるかに狭い事実です。そのコマンドが確認したのは、証憑文書が署名されたあとで変わっていないことだけで、成果物は、コマンドに登場すらしません。 証憑と成果物をつなぐのは、subjectのdigestです。そのため、文書を書き換えられる側が署名も付け直せる状況では、フィンガープリントを計算し直して突き合わせる1行が、手順の唯一の防御になります。 このラボは、その1行がないときに何が起きるかを、自分で作って確かめます。署名が有効なまま、subjectだけがすでに承認された旧バージョンを指す証憑を作り、署名の層と紐付けの層が別のことを言っていることを、記録に残します。 最後のステップの報告書は、形式合わせではありません。確認したことと、このネットワークでは確認する方法がないことを分けておかなければ、読む人にはすべて確認されたことと読まれ、その誤解が承認の署名にまでついていきます。
ステップ
- 生成スクリプトをそのまま回して、
/root/supply/drop/の下に成果物を、/root/supply/evidence/の下に証憑5枚を、そして署名2つを作ってください。 - 成果物のフィンガープリントを計算し直してStatementのsubjectと突き合わせ、
/root/supply/check/subject.txtに書いてください。 - SBOMのコンポーネント一覧と実際のツリーを双方向で突き合わせて、
/root/supply/check/sbom-diff.jsonに書いてください。 - Provenanceから事実を抜き出し、
/root/supply/QUESTIONS.txtの質問6つを、/root/supply/check/provenance.jsonで仕分けてください。 openssl dgst -sha256 -verifyで証憑の署名を確認し、/root/supply/check/signature.txtに書いてください。/root/supply/swap/statement-swapped.jsonに、署名は有効なのにsubjectが旧バージョンを指す証憑を作り、結果を/root/supply/check/swap.txtに2つの層で書いてください。- 証憑3枚の述語を見て、
/root/supply/check/predicates.jsonにaccept・hold・rejectを仕分けてください。 /root/supply/check/attestation-report.jsonに、持ち込み審査用の証憑突き合わせ報告書を書いてください。
参考
- 素材のうち、画面に例を載せていないものは、質問一覧の/root/supply/QUESTIONS.txt、報告書に使う語の一覧の/root/supply/REPORT-KEYS.txt、発注元の署名鍵の/root/supply/.vendor/vendor-release.key、公開鍵の/root/supply/evidence/vendor-release.pubです。すべてステップ1の生成スクリプトが作ります。
- フィンガープリントの再計算:
sha256sum /root/supply/drop/collector-2.4.1.tar.gz - 署名の確認:
openssl dgst -sha256 -verify /root/supply/evidence/vendor-release.pub -signature /root/supply/evidence/statement.json.sig /root/supply/evidence/statement.json - JSONは
python3 -cかjqで読みます。このPodにはインターネットもpipもないので、標準ライブラリだけを使います。 - よくある間違いは、subjectのnameだけを見て合っていると判断すること、SBOMを片方向にしか突き合わせないこと、
metadata.componentをコンポーネントとして数えること、知らない述語を無視して通過として数えることです。 - 規格の原文: in-toto Statement https://github.com/in-toto/attestation/blob/main/spec/v1/statement.md と SLSA Provenance https://slsa.dev/spec/v1.0/provenance と CycloneDX https://cyclonedx.org/specification/overview/
成果物と証憑のセットをネットワークの中に再現する
生成スクリプトをそのまま回して、/root/supply/drop/collector-2.4.1.tar.gzと/root/supply/drop/payload/の7ファイル、/root/supply/archive/collector-2.4.0.tar.gz、そして/root/supply/evidence/の下のsbom.cdx.json・statement.json・qa-signoff.json・vendor-release.pubと、署名2つを作ってください。/root/supply/QUESTIONS.txtと/root/supply/REPORT-KEYS.txtも一緒に作られます。
スクリプトは、シードとセットの時刻を固定してあります。そうすれば、何回回しても同じフィンガープリントが出て、次のステップの突き合わせが、人によって変わりません。スクリプトを直さず、そのまま使ってください。署名鍵は、すでにあれば作り直しません。
受け取ったファイルのフィンガープリントを計算し直してsubjectと突き合わせる
/root/supply/check/subject.txtに、成果物のフィンガープリント、Statementのsubjectの名前とフィンガープリント、2つのフィンガープリントの一致の有無、_type、predicateTypeを書いてください。
subjectは配列です。要素ごとにnameとdigestがあり、規格は、digestが必ずなければならないと定めています。名前は区別のための付箋なので、突き合わせても何も確認されません。一致の有無は、YESまたはNOの1語で書きます。
SBOMと実際のツリーを双方向で突き合わせる
/root/supply/check/sbom-diff.jsonに、SBOMのコンポーネント数、ツリーのファイル数、完全一致の数、文書にだけある一覧、ツリーにだけある一覧、フィンガープリントがずれた一覧を書いてください。
ずれには3種類あります。文書にだけあるもの、ツリーにだけあるもの、名前は両側にあるのにフィンガープリントが違うもの。一覧だけを突き合わせる照合は、3つ目をまるごと見逃します。パスはpayload基準の相対パスで書き、コンポーネントは、typeがfileのものだけを数えます。
Provenanceで答えられる質問と答えられない質問を分ける
/root/supply/check/provenance.jsonに、buildType、builder.id、invocationId、開始・終了時刻、externalParametersのキー一覧、resolvedDependenciesの個数を書き、/root/supply/QUESTIONS.txtの質問6つを、answerableとnot-answerableに仕分けてください。
判定基準は1つです。predicateの中に、その事実を書く場所が実際にあるか。仕様は、externalParametersを下流で検証するよう、internalParametersは、プラットフォームを信頼しているので検証する必要がないと、分けて書いています。キー一覧に、2つを混ぜないでください。
署名が何を覆って何を覆わないかを確認する
openssl dgst -sha256 -verifyで2つの文書の署名を確認し、/root/supply/check/signature.txtに、公開鍵のフィンガープリント、2つの検証結果、署名が付いた文書の数と付いていない文書の数、署名が成果物自体を覆うかどうかを書いてください。
検証コマンドに、どのファイルを入力として与えたかを見てください。成果物は、そのコマンドに登場しません。公開鍵のフィンガープリントは、PEMではなく、DERに変換してから取ります。PEMはヘッダーと改行が混ざっていて、同じ鍵なのにファイルごとにハッシュが変わることがあります。
署名は有効なのにsubjectが別のものを指す証憑を作る
/root/supply/swap/statement-swapped.jsonに、述語はそのままで、subjectだけが/root/supply/archive/collector-2.4.0.tar.gzを指すように変えた証憑を作り、同じ鍵で署名し直して、結果を/root/supply/check/swap.txtに2つの層で書いてください。
元の証憑には手を付けず、別の複製として作ります。文書を直したあと、署名し直してはじめて、「署名が有効なのに」という反例が成り立ちます。述語まで変えると、次のステップの「知らない述語」の問題と混ざって、要点がぼやけます。フィンガープリントは、Statementから移さず、そのファイルから計算し直して確認してください。
知らない述語を、通過でも拒否でもない場所に置く
/root/supply/check/predicates.jsonに、私たちが読み方を知っている述語の一覧と、証憑3枚(evidence/statement.json・evidence/qa-signoff.json・swap/statement-swapped.json)の、述語・知っている述語か・subjectの一致の有無・判定を書いてください。
判定は、2つの値から導かれます。読み方を知らない述語は、通過の根拠にはなれず、かといって、それ自体が拒否の根拠でもありません。in-totoのパースルールは、ポリシーを「悪い証憑があれば止める」ではなく「良い証憑があってはじめて通過」と書くよう勧めています。判定の値は、accept・hold・rejectの3つだけです。
持ち込み審査に添付する証憑突き合わせ報告書を書く
/root/supply/check/attestation-report.jsonに、成果物とそのフィンガープリント、/root/supply/evidence/の下の6ファイルのフィンガープリント、確認したことと確認できなかったこと、ステップ3で数えた3種類のずれ、ステップ7で保留した証憑、そして判定を書いてください。
verifiedとnot_verifiedには、/root/supply/REPORT-KEYS.txtの語8個を、漏れなく、重ならないように分けて入れます。分ける基準は1つです。このPodの中で、私が直接計算したか、検証したか。他人が文書に書いてくれた主張は、確認ではありません。判定の3つの値の意味も、同じファイルに書かれています。