DNSSECの基本的な検証機能とその実装
DNSSEC - RRset の署名検証 - RRSIG(Resource Record Signature)
DNSSEC は RRset( RR(リソースレコード)のセット ) の内容を、利用者から検証可能にするための仕組みです.
DNSKEY RRには、そのゾーンで利用する公開鍵が登録され、RRSIG RR には RRset のタイプ(署名対象タイプ)とその署名値が登録されます.
RRset の利用者は、RRSIG RR が持つ署名値と DNSKEY RR が持つ公開鍵で署名検証を行なうことで、RRset がゾーンの管理者が署名した内容であることを確認することができます.
検証アルゴリズムでは、入力として RR の正規化ワイヤーフォーマット1の列2を処理することで、表現のゆれや圧縮の有無による不一致が起きないようにしています.
DNSSEC - 委任情報の検証 - DS(Delegation Signer)
委任元ゾーンでは、 DS RR に委任先ゾーンの DNSKEY RRのダイジェスト値が登録されるとともに、 DSタイプを署名対象にした RRSIG RR が登録されます.
これは、委任元がDS RRを介して委任先を署名していることを意味します.
DS RRが保持しているのは秘密鍵が無くとも計算できる DNSKEY RRのダイジェスト値であるため、 利用者が DS RRの内容を確認するには、RRSIG RRを使って検証する必要があります. DS RRとそれに対応するRRSIG RRは委任元のゾーンにあるため、検証は委任元の DNSKEY で行なうことになります.
DNSSEC - 不在証明 - NSEC3(NextSECure v.3)
NSEC3は、ドメインの不在を示すための仕組みです. ハッシュ化ドメイン名を使用することで、ゾーン全体の情報を簡単には取得できないようにしています.
NSEC3 RRは、ハッシュ化ドメイン名を所有者とし、そのゾーン内で存在する次のハッシュ化ドメイン名の先頭のラベルを保持します.
少し複雑ですが、このレコードは、以下を示しています.
- 所有者のハッシュ化ドメイン名のハッシュ化前のドメイン名が存在している.
- 次に存在するハッシュ化ドメイン名のハッシュ化前のドメイン名が存在している.
- ハッシュ化ドメイン名が、所有者のハッシュ化ドメイン名と次のハッシュ化ドメイン名の間となった場合、ハッシュ化前のドメイン名が存在しない.
ここでハッシュ化ドメイン名とハッシュ化ラベルは次の操作で計算します.
- 元のドメイン名の正規化ワイヤーフォーマット(RR内のドメイン名の正規化と同じ)を入力としてハッシュ値を計算3
- ハッシュ値を Base32Hex4 でエンコードし、アルファベットは小文字にする. これをハッシュ化ラベルとする
- ハッシュ化ラベルを先頭のラベルとしてゾーンのドメイン名を補う. これをハッシュ化ドメイン名とする
ハッシュ化後の比較に利用する順序関係には、DNS名の正規順序5を使用します.
NSEC3 RR はタイプビットマップを持っています. このフィールドから、所有者名が持つ RRset のタイプの一覧がわかるので、 ドメイン名は存在するけれども、タイプが無い NODATAの場合を示すこともできます.
このように、NSEC3 RR が有ることで、否定的な応答6を示すことができますが、 利用者がその内容を確認するには、対応する RRSIG RR で署名検証する必要があります.
DNSSEC - 不在証明 - NSEC(NextSECure)
NSECも、ドメインの不在を示すための仕組みです. ルートゾーンのような、ゾーン全体が公開されている場合に利用されています.
NSEC RRは、そのゾーン内で所有者のドメイン名の次のドメイン名を保持します. このレコードは、所有者のドメイン名と次のドメイン名の間にはドメイン名が存在しないことを示しています.
比較に利用する順序関係には、やはりDNS名の正規順序を使用します.
ハッシュ化が無い分 NSEC3 よりも簡単です.
NSEC3 RRと同様に、NSEC RR もタイプビットマップを持っています.
確認のために RRSIG RR で署名検証が必要なのも、NSEC3 の場合と同様です.
DNSSEC 検証機能の実装
dnsextライブラリ群7の dnsext-dnssec に DNSSEC の各検証機能を実装しました8
DNSSEC では、暗号アルゴリズムに割り当てられた番号ごとに、アルゴリズムを切り替えて処理を行ないます. アルゴリズムの追加拡張を可能にするために、暗号ライブラリモジュールごとに異なっている公開鍵の型や署名の型を、抽象化して吸収する必要があります.
GHC の ExistensialQuantification の拡張機能で、この抽象化をうまく行なうことができました. たとえば、RRSIG を検証するインターフェースは次のような存在型を利用しています.
data RRSIGImpl = forall pubkey sig . RRSIGImpl { rrsigIGetKey :: PubKey -> Either String pubkey , rrsigIGetSig :: Opaque -> Either String sig , rrsigIVerify :: pubkey -> sig -> ByteString -> Either String Bool }
存在型 pubkey
と sig
のところに暗号ライブラリ依存の公開鍵の型や署名の型を当て嵌めることで、
暗号ライブラリの型と切り離したインターフェースで、検証機能を実装することができました.
- Canonical RR Form https://datatracker.ietf.org/doc/html/rfc4034#section-6.2↩
- 次の定義で整列させます: Canonical RR Ordering within an RRset https://datatracker.ietf.org/doc/html/rfc4034#section-6.3↩
- Calculation of the Hash https://datatracker.ietf.org/doc/html/rfc5155#section-5↩
- Base 32 Encoding with Extended Hex Alphabet https://www.rfc-editor.org/rfc/rfc4648#section-7↩
- Canonical DNS Name Order https://datatracker.ietf.org/doc/html/rfc4034#section-6.1↩
- 否定応答については https://khibino.hatenadiary.jp/entry/2023/03/20/105555 を参照↩
- https://github.com/kazu-yamamoto/dnsext↩
- インターフェース https://github.com/khibino/dnsext/blob/dnssec-verify-impl/dnsext-dnssec/DNS/SEC/Verify/Types.hs, 実装 https://github.com/khibino/dnsext/blob/dnssec-verify-impl/dnsext-dnssec/DNS/SEC/Verify/Verify.hs↩