[インデックス 13884] ファイルの概要
このコミットは、Go言語の標準ライブラリである crypto/x509
パッケージ内の、X.509証明書のName Constraints拡張処理に関する修正です。具体的には、証明書内の非クリティカルなメールアドレス制約(email constraints)を解析する際に、RFC 5280の仕様に準拠し、本来使用されない Min
および Max
フィールドの値を無視するように変更されています。これにより、誤った解析による不必要なエラー発生を防ぎ、コードの複雑性を軽減しています。
コミット
commit be11889ab1584a5c6dfd1422ba163d893753b1ad
Author: Adam Langley <agl@golang.org>
Date: Thu Sep 20 12:30:56 2012 -0400
crypto/x509: ignore non-critical email constraints
Previously we tried to parse email constraints as the maximum base
distance (which is unused and must be omitted according to RFC 5280)
because they share the same tag number. Rather than complicate the
code further, this CL just ignores the unused values.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6552044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/be11889ab1584a5c6dfd1422ba163d893753b1ad
元コミット内容
crypto/x509: ignore non-critical email constraints
Previously we tried to parse email constraints as the maximum base
distance (which is unused and must be omitted according to RFC 5280)
because they share the same tag number. Rather than complicate the
code further, this CL just ignores the unused values.
変更の背景
この変更の背景には、X.509証明書のName Constraints拡張の解析における、ASN.1(Abstract Syntax Notation One)のタグ付けとRFC 5280の仕様解釈の複雑さがありました。
X.509証明書には、証明書が発行できるサブジェクト名空間を制限するための「Name Constraints」という拡張があります。この拡張は、GeneralSubtree
という構造体のリストで構成され、各GeneralSubtree
は特定の名前空間(例: DNS名、IPアドレス、メールアドレスなど)とその制約を定義します。
問題は、GeneralSubtree
構造体には、特定の種類の制約(特にディレクトリ名やIPアドレス)に対して「base distance」と呼ばれる Min
と Max
のフィールドが存在することです。これらのフィールドは、制約が適用される名前空間の深さや範囲を示すために使用されます。しかし、RFC 5280(X.509証明書とCRLのインターネットプロファイル)のセクション4.2.1.10「Name Constraints」では、メールアドレス、DNS名、URIなどの制約タイプの場合、これらの Min
および Max
フィールドは「未使用であり、省略されなければならない (unused and must be omitted)」と明確に規定されています。
以前の実装では、ASN.1のタグ番号の共有(または誤解釈)により、メールアドレス制約を解析する際に、本来存在しないはずの Min
や Max
の値が誤って読み取られる可能性がありました。もしこれらの値が非ゼロで、かつName Constraints拡張がクリティカル(Critical)としてマークされていた場合、Goの crypto/x509
パッケージは「UnhandledCriticalExtension」エラーを発生させていました。これは、仕様上は無視すべき値が原因で、証明書の検証が失敗するという不適切な挙動でした。
このコミットは、このような誤った解析とそれに伴うエラーを回避し、RFC 5280の規定に厳密に準拠するために行われました。コードをさらに複雑にすることなく、未使用の値を単に無視するというアプローチが取られています。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念を把握しておく必要があります。
- X.509 証明書: 公開鍵基盤 (PKI) において、公開鍵と所有者の身元を結びつけるデジタル文書です。様々な情報(発行者、有効期間、公開鍵など)を含み、拡張機能によって追加の情報を付与できます。
- Name Constraints 拡張: X.509証明書の拡張の一つで、証明書が発行できるサブジェクト名(例: ドメイン名、メールアドレス、IPアドレス)の範囲を制限するために使用されます。例えば、あるCAが特定のドメイン(例:
example.com
)とそのサブドメインにのみ証明書を発行することを許可する場合に利用されます。 - ASN.1 (Abstract Syntax Notation One): データの構造を定義するための標準的な記法であり、X.509証明書のような複雑なデータ構造をシリアライズ(バイト列に変換)するために広く使用されます。ASN.1は、データ型、タグ、長さ、値のエンコーディングルール(例: DER, BER)を定義します。
- タグ: ASN.1では、各データ要素にタグが割り当てられます。タグは、そのデータ要素の型を識別するために使用されます。コンテキスト固有のタグ(
[0]
,[1]
など)は、同じ基本型でも異なる意味を持つ場合に使用されます。
- タグ: ASN.1では、各データ要素にタグが割り当てられます。タグは、そのデータ要素の型を識別するために使用されます。コンテキスト固有のタグ(
- RFC 5280: 「Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile」という標準文書です。X.509証明書とCRLの具体的なプロファイル(実装上の詳細なルール)を定義しており、Name Constraints拡張の構造とセマンティクスについても詳細に記述されています。
GeneralSubtree
構造体: Name Constraints拡張内で使用されるASN.1構造体で、特定の名前空間の制約を表現します。これには、制約される名前(Name
フィールド)と、オプションでその名前空間の深さや範囲を示すMin
およびMax
フィールドが含まれます。Min
とMax
(base distance): これらのフィールドは、GeneralSubtree
がディレクトリ名やIPアドレスの制約を表す場合に、その名前空間の深さ(Min
)や、そこから許容されるサブツリーの深さ(Max
)を定義するために使用されます。しかし、RFC 5280によれば、メールアドレス、DNS名、URIなどの他の種類の名前制約では、これらのフィールドは「未使用であり、省略されなければならない」とされています。
- クリティカル拡張 (Critical Extensions): X.509証明書の拡張には、「クリティカル (Critical)」または「非クリティカル (Non-Critical)」のフラグが設定されます。
- クリティカル拡張: 証明書を処理するアプリケーションが、その拡張の内容を理解し、適切に処理しなければならないことを示します。もしアプリケーションがクリティカル拡張を理解できない場合、またはその内容を適切に処理できない場合、その証明書は無効とみなされ、検証は失敗しなければなりません。
- 非クリティカル拡張: アプリケーションがその拡張の内容を理解できない場合でも、証明書の検証を続行できることを示します。理解できない拡張は無視しても構いません。 このコミットでは、非クリティカルなメールアドレス制約が問題となっていました。
技術的詳細
このコミットの技術的な核心は、crypto/x509
パッケージがX.509証明書内の Name Constraints
拡張をどのように解析し、特に GeneralSubtree
構造体内の Min
および Max
フィールドをどのように扱うかという点にあります。
Goの crypto/x509
パッケージでは、ASN.1で定義されたX.509証明書の構造をGoの構造体にマッピングして解析します。generalSubtree
構造体は、ASN.1の GeneralSubtree
を表現するために定義されていました。
変更前の generalSubtree
構造体は以下のようになっていました。
type generalSubtree struct {
Name string `asn1:"tag:2,optional,ia5"`
Min int `asn1:"optional,tag:0"`
Max int `asn1:"optional,tag:1"`
}
ここで注目すべきは、Min
と Max
フィールドに asn1:"optional,tag:0"
および asn1:"optional,tag:1"
というASN.1タグが付けられている点です。これらのタグは、ASN.1のコンテキスト固有タグ(Context-specific tags)であり、GeneralSubtree
の定義において base
と subtree
の距離を示すために使用されます。
RFC 5280のセクション4.2.1.10では、GeneralSubtree
の定義において、base
フィールド(名前自体)に加えて、minimum
と maximum
というオプションのフィールドが定義されています。これらはそれぞれ [0] IMPLICIT INTEGER DEFAULT 0
と [1] IMPLICIT INTEGER OPTIONAL
というASN.1定義を持ちます。
問題は、メールアドレス制約のような特定の名前タイプの場合、RFC 5280がこれらの minimum
および maximum
フィールドを「未使用であり、省略されなければならない」と規定しているにもかかわらず、GoのASN.1パーサーが、何らかの理由(例えば、ASN.1エンコーディングの曖昧さや、他のフィールドとのタグの衝突、あるいは単にパーサーの厳密性の欠如)で、これらのフィールドに誤った非ゼロの値を読み込んでしまう可能性があったことです。
もし Min
や Max
に非ゼロの値が読み込まれ、かつその Name Constraints
拡張がクリティカルとしてマークされていた場合、parseCertificate
関数内の以下のチェックがトリガーされていました。
if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 {
if e.Critical {
return out, UnhandledCriticalExtension{}
}
// ... (非クリティカルの場合は無視)
}
このコードは、「Min
または Max
が非ゼロであるか、Name
が空である」という条件が満たされ、かつ拡張がクリティカルである場合に UnhandledCriticalExtension
エラーを返していました。メールアドレス制約の場合、Min
や Max
は常にゼロ(または省略)であるべきなので、もし非ゼロの値が誤って読み込まれた場合、このチェックによって不適切なエラーが発生していました。
このコミットは、この問題を根本的に解決するために、generalSubtree
構造体から Min
と Max
フィールドを削除し、それに伴い関連するチェックも削除するというアプローチを取りました。これにより、GoのASN.1パーサーはこれらのフィールドを読み込もうとせず、結果として誤った値が設定されることもなくなります。
コアとなるコードの変更箇所
変更は src/pkg/crypto/x509/x509.go
ファイルの2箇所です。
-
generalSubtree
構造体の定義変更:--- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -613,8 +613,6 @@ type nameConstraints struct { type generalSubtree struct { Name string `asn1:"tag:2,optional,ia5"` - Min int `asn1:"optional,tag:0"` - Max int `asn1:"optional,tag:1"` }
generalSubtree
構造体からMin
とMax
フィールドが削除されました。 -
parseCertificate
関数内の条件分岐の変更:--- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -831,7 +829,7 @@ func parseCertificate(in *certificate) (*Certificate, error) { for _, subtree := range constraints.Permitted { - if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 { + if len(subtree.Name) == 0 { if e.Critical { return out, UnhandledCriticalExtension{} }
parseCertificate
関数内で、constraints.Permitted
リストの各subtree
を処理するループ内の条件式が変更されました。以前はsubtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0
でしたが、subtree.Min > 0 || subtree.Max > 0
の部分が削除され、len(subtree.Name) == 0
のみになりました。
コアとなるコードの解説
このコミットの核心は、X.509証明書のName Constraints拡張における GeneralSubtree
の解析ロジックを、RFC 5280の仕様に厳密に合わせることにあります。
-
generalSubtree
構造体からのMin
とMax
フィールドの削除: この変更が最も重要です。Min
とMax
フィールドは、GeneralSubtree
がディレクトリ名やIPアドレスの制約を表す場合にのみ意味を持つ「base distance」を示すためのものです。メールアドレス制約のような他の名前タイプでは、RFC 5280によってこれらのフィールドは「未使用であり、省略されなければならない」と規定されています。 以前の実装では、これらのフィールドが構造体内に存在し、ASN.1パーサーが証明書からこれらの値を読み取ろうとしていました。もし証明書がRFC 5280に準拠していないか、あるいはパーサーが誤って非ゼロの値を読み取ってしまった場合、それが問題を引き起こしていました。 これらのフィールドを構造体から完全に削除することで、GoのASN.1パーサーは、メールアドレス制約を解析する際に、これらの「未使用かつ省略されるべき」フィールドの値を読み取ろうとすること自体がなくなります。これにより、誤った値が設定される可能性が根本的に排除されます。 -
parseCertificate
関数内の条件分岐の簡素化:generalSubtree
構造体からMin
とMax
フィールドが削除されたため、これらのフィールドを参照する条件式subtree.Min > 0 || subtree.Max > 0
はもはや有効ではありません。 この部分が削除され、条件式はlen(subtree.Name) == 0
のみになりました。これは、GeneralSubtree
において名前(Name
)が空である場合に、それがクリティカル拡張であればUnhandledCriticalExtension
エラーを返すというロジックです。 この変更により、メールアドレス制約の解析において、本来無視すべきMin
やMax
の値が原因で不必要にUnhandledCriticalExtension
エラーが発生することがなくなります。証明書の検証は、RFC 5280の規定通り、名前が空であるかどうかに基づいてのみ行われるようになります。
要するに、このコミットは、X.509証明書の解析において、RFC 5280の仕様に厳密に準拠し、特にメールアドレス制約のような特定のName Constraintsタイプにおいて、本来存在しないはずの Min
および Max
フィールドの誤った解釈による問題を解消しています。これにより、証明書の検証の堅牢性と正確性が向上しました。
関連リンク
- Go CL 6552044: https://golang.org/cl/6552044
参考にした情報源リンク
- RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile
- 特にセクション 4.2.1.10. Name Constraints: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
- ASN.1 (Abstract Syntax Notation One) の基本概念
- X.509 証明書の構造と拡張に関する一般的な情報