ディスカッション (11件)
データ構造にデジタル署名を付与する際、単純にシリアライズして署名するだけでは不十分な場合があります。正規化(Canonicalization)の欠如や署名対象の範囲の誤りなど、一見正しそうに見えて実は脆弱な「間違った署名のやり方」について、そのリスクと注意点をまとめました。
IDLにドメインセパレーターを入れるのは面白いけど、インバンド(常に存在する「型」フィールドのようなもの)にドメインセパレーターを入れることでも解決できるよ。
余談だけど、入力やデータモデルにもよるけど、正規化にはO(nlogn)の時間(フィールドをソートするコスト)がかかるね。
マルチセットハッシュを使って、明示的な正規化ステップなしで決定論的なハッシュを生成する別のアプローチをここに書いたから参考にしてみて: https://www.da.vidbuchanan.co.uk/blog/signing-json.html
protoで例が出ていたから、protoでの解決策を提案するよ。メッセージオプションを追加すればいい。
extend google.protobuf.MessageOptions {
optional uint64 domain_separator = 1234;
}
message TreeRoot {
option (domain_separator) = 4567;
...
}
この記事では、これらがまだ未解決の問題のように書かれているけど、ずっと前から解決済みのことだよ。
#1 BLOBに署名したら、署名を検証するまでその中身を触らない(いわゆる「暗号学的破滅原則」)
#2 署名は、送信はされないが鍵の導出に使われたり、MACに混ぜ込まれたりする「コンテキスト」に紐付けられる。これはHorton原則と呼ばれるもの。これにより、署名者と検証者はメッセージが意図するコンテキストについて「暗号学的に」合意しなければならなくなる。これを正しく実装しないと、すべての署名の検証が失敗するから、間違った実装は本質的にできないようになっている。
この記事は、まさに原則#2に違反する(マジックナンバーをプロトコルヘッダーに埋め込んで、誰かがそれをチェックしてくれるだろうと想定する)やり方を提案している。これは設計として不適切で、歴史的に見ても悪い結果を招くだけだよ。
原則#1と#2は、それぞれ数十年にわたって確立された暗号設計原則なんだ。
またしてもASN.1から学んだ教訓を学び直したわけか。この業界で働いているのが誇らしいよ!次は、データには常にバージョンを含めるべきだってことに気付くんだろうね。
当たり前のことをうまく説明しているね。ドメイン分離も、ドメイン識別子をIDLに入れることもどちらもいいけど、目新しい話じゃない。
暗号は難しい。正しく実装すること。ツールを活用すること。それだけだよ。
まったく、こんなことのために自分は年を取りすぎたよ。
つまり、署名はそのメッセージが含まれるスコープにしか及ばない、って言っているだけでしょ?
「はい」という言葉に署名しても、何の質問に対する返答なのか分からなければ意味がない。署名には、それが意味を持つために必要なコンテキストが含まれていないと。
やり方はいくらでもあるけど、冗長なデータやストレージのオーバーヘッドについては慎重に考える必要があるね。概念自体はトリッキーじゃないよ。
型をハッシュの一部として含めるのはどう?そうすれば記事の問題は避けられるし、データサイズも小さく済むでしょ。
自分のデータ構造をネットワーク経由で送信するメッセージにする時は、いつも固定幅フィールドのmsgIdとmsgLenから始めるようにしているよ。
これならメッセージの識別問題を明示的に解決できるし、セキュリティやメモリ管理も簡単になる。ルーティングもこんな感じに減らせるしね。
switch(msg.msgId):
…
名目型(nominal)か構造的部分型(structural)かという点が核心だね。コードを書いている時は型が具体的で、型が違えばコンパイラが怒ってくれるから直感的だけど、シリアライズした途端にその区別が失われるということに気付きにくいんだ。
DSSEはこれに最適だよ。スキーマがもっと必要ならin-totoを使えばいい。