Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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」と呼ばれる MinMax のフィールドが存在することです。これらのフィールドは、制約が適用される名前空間の深さや範囲を示すために使用されます。しかし、RFC 5280(X.509証明書とCRLのインターネットプロファイル)のセクション4.2.1.10「Name Constraints」では、メールアドレス、DNS名、URIなどの制約タイプの場合、これらの Min および Max フィールドは「未使用であり、省略されなければならない (unused and must be omitted)」と明確に規定されています。

以前の実装では、ASN.1のタグ番号の共有(または誤解釈)により、メールアドレス制約を解析する際に、本来存在しないはずの MinMax の値が誤って読み取られる可能性がありました。もしこれらの値が非ゼロで、かつName Constraints拡張がクリティカル(Critical)としてマークされていた場合、Goの crypto/x509 パッケージは「UnhandledCriticalExtension」エラーを発生させていました。これは、仕様上は無視すべき値が原因で、証明書の検証が失敗するという不適切な挙動でした。

このコミットは、このような誤った解析とそれに伴うエラーを回避し、RFC 5280の規定に厳密に準拠するために行われました。コードをさらに複雑にすることなく、未使用の値を単に無視するというアプローチが取られています。

前提知識の解説

このコミットを理解するためには、以下の技術的な概念を把握しておく必要があります。

  1. X.509 証明書: 公開鍵基盤 (PKI) において、公開鍵と所有者の身元を結びつけるデジタル文書です。様々な情報(発行者、有効期間、公開鍵など)を含み、拡張機能によって追加の情報を付与できます。
  2. Name Constraints 拡張: X.509証明書の拡張の一つで、証明書が発行できるサブジェクト名(例: ドメイン名、メールアドレス、IPアドレス)の範囲を制限するために使用されます。例えば、あるCAが特定のドメイン(例: example.com)とそのサブドメインにのみ証明書を発行することを許可する場合に利用されます。
  3. ASN.1 (Abstract Syntax Notation One): データの構造を定義するための標準的な記法であり、X.509証明書のような複雑なデータ構造をシリアライズ(バイト列に変換)するために広く使用されます。ASN.1は、データ型、タグ、長さ、値のエンコーディングルール(例: DER, BER)を定義します。
    • タグ: ASN.1では、各データ要素にタグが割り当てられます。タグは、そのデータ要素の型を識別するために使用されます。コンテキスト固有のタグ([0], [1]など)は、同じ基本型でも異なる意味を持つ場合に使用されます。
  4. RFC 5280: 「Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile」という標準文書です。X.509証明書とCRLの具体的なプロファイル(実装上の詳細なルール)を定義しており、Name Constraints拡張の構造とセマンティクスについても詳細に記述されています。
  5. GeneralSubtree 構造体: Name Constraints拡張内で使用されるASN.1構造体で、特定の名前空間の制約を表現します。これには、制約される名前(Nameフィールド)と、オプションでその名前空間の深さや範囲を示す Min および Max フィールドが含まれます。
    • MinMax (base distance): これらのフィールドは、GeneralSubtreeがディレクトリ名やIPアドレスの制約を表す場合に、その名前空間の深さ(Min)や、そこから許容されるサブツリーの深さ(Max)を定義するために使用されます。しかし、RFC 5280によれば、メールアドレス、DNS名、URIなどの他の種類の名前制約では、これらのフィールドは「未使用であり、省略されなければならない」とされています。
  6. クリティカル拡張 (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"`
}

ここで注目すべきは、MinMax フィールドに asn1:"optional,tag:0" および asn1:"optional,tag:1" というASN.1タグが付けられている点です。これらのタグは、ASN.1のコンテキスト固有タグ(Context-specific tags)であり、GeneralSubtree の定義において basesubtree の距離を示すために使用されます。

RFC 5280のセクション4.2.1.10では、GeneralSubtree の定義において、base フィールド(名前自体)に加えて、minimummaximum というオプションのフィールドが定義されています。これらはそれぞれ [0] IMPLICIT INTEGER DEFAULT 0[1] IMPLICIT INTEGER OPTIONAL というASN.1定義を持ちます。

問題は、メールアドレス制約のような特定の名前タイプの場合、RFC 5280がこれらの minimum および maximum フィールドを「未使用であり、省略されなければならない」と規定しているにもかかわらず、GoのASN.1パーサーが、何らかの理由(例えば、ASN.1エンコーディングの曖昧さや、他のフィールドとのタグの衝突、あるいは単にパーサーの厳密性の欠如)で、これらのフィールドに誤った非ゼロの値を読み込んでしまう可能性があったことです。

もし MinMax に非ゼロの値が読み込まれ、かつその Name Constraints 拡張がクリティカルとしてマークされていた場合、parseCertificate 関数内の以下のチェックがトリガーされていました。

if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 {
    if e.Critical {
        return out, UnhandledCriticalExtension{}
    }
    // ... (非クリティカルの場合は無視)
}

このコードは、「Min または Max が非ゼロであるか、Name が空である」という条件が満たされ、かつ拡張がクリティカルである場合に UnhandledCriticalExtension エラーを返していました。メールアドレス制約の場合、MinMax は常にゼロ(または省略)であるべきなので、もし非ゼロの値が誤って読み込まれた場合、このチェックによって不適切なエラーが発生していました。

このコミットは、この問題を根本的に解決するために、generalSubtree 構造体から MinMax フィールドを削除し、それに伴い関連するチェックも削除するというアプローチを取りました。これにより、GoのASN.1パーサーはこれらのフィールドを読み込もうとせず、結果として誤った値が設定されることもなくなります。

コアとなるコードの変更箇所

変更は src/pkg/crypto/x509/x509.go ファイルの2箇所です。

  1. 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 構造体から MinMax フィールドが削除されました。

  2. 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の仕様に厳密に合わせることにあります。

  1. generalSubtree 構造体からの MinMax フィールドの削除: この変更が最も重要です。MinMax フィールドは、GeneralSubtree がディレクトリ名やIPアドレスの制約を表す場合にのみ意味を持つ「base distance」を示すためのものです。メールアドレス制約のような他の名前タイプでは、RFC 5280によってこれらのフィールドは「未使用であり、省略されなければならない」と規定されています。 以前の実装では、これらのフィールドが構造体内に存在し、ASN.1パーサーが証明書からこれらの値を読み取ろうとしていました。もし証明書がRFC 5280に準拠していないか、あるいはパーサーが誤って非ゼロの値を読み取ってしまった場合、それが問題を引き起こしていました。 これらのフィールドを構造体から完全に削除することで、GoのASN.1パーサーは、メールアドレス制約を解析する際に、これらの「未使用かつ省略されるべき」フィールドの値を読み取ろうとすること自体がなくなります。これにより、誤った値が設定される可能性が根本的に排除されます。

  2. parseCertificate 関数内の条件分岐の簡素化: generalSubtree 構造体から MinMax フィールドが削除されたため、これらのフィールドを参照する条件式 subtree.Min > 0 || subtree.Max > 0 はもはや有効ではありません。 この部分が削除され、条件式は len(subtree.Name) == 0 のみになりました。これは、GeneralSubtree において名前(Name)が空である場合に、それがクリティカル拡張であれば UnhandledCriticalExtension エラーを返すというロジックです。 この変更により、メールアドレス制約の解析において、本来無視すべき MinMax の値が原因で不必要に UnhandledCriticalExtension エラーが発生することがなくなります。証明書の検証は、RFC 5280の規定通り、名前が空であるかどうかに基づいてのみ行われるようになります。

要するに、このコミットは、X.509証明書の解析において、RFC 5280の仕様に厳密に準拠し、特にメールアドレス制約のような特定のName Constraintsタイプにおいて、本来存在しないはずの Min および Max フィールドの誤った解釈による問題を解消しています。これにより、証明書の検証の堅牢性と正確性が向上しました。

関連リンク

参考にした情報源リンク

  • RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile
  • ASN.1 (Abstract Syntax Notation One) の基本概念
  • X.509 証明書の構造と拡張に関する一般的な情報