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

[インデックス 14378] ファイルの概要

このコミットは、Go言語のencoding/pemパッケージにおいて、PEM (Privacy-Enhanced Mail) 形式のエンコード時に、Proc-TypeヘッダーがRFC 1421の仕様に従って常に最初に書き込まれるように修正するものです。これにより、生成されるPEMブロックの互換性と標準への準拠が向上します。

コミット

  • コミットハッシュ: 1e1733a9ac8865d75403494bd29bef0cdec44fbc
  • Author: Roger Peppe rogpeppe@gmail.com
  • Date: Mon Nov 12 15:29:17 2012 +0000
  • コミットメッセージの要約: encoding/pem: write Proc-Type header first.

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/1e1733a9ac8865d75403494bd29bef0cdec44fbc

元コミット内容

encoding/pem: write Proc-Type header first.
See RFC 1421, section 4.6.1.1

R=agl, agl
CC=golang-dev
https://golang.org/cl/6814104

変更の背景

PEM (Privacy-Enhanced Mail) 形式は、公開鍵証明書や秘密鍵などの暗号化データをASCII形式で表現するための標準的な方法です。PEM形式は、-----BEGIN XXX----------END XXX-----という境界線で囲まれたBase64エンコードされたデータと、その間にオプションのヘッダー情報を含みます。

RFC 1421は、PEMの初期の仕様の一つであり、特にセクション4.6.1.1では「Proc-Type Field」について規定しています。この規定には、Proc-Typeヘッダーが他のヘッダーよりも前に記述されるべきであるという要件が含まれています。

このコミット以前のencoding/pemパッケージの実装では、ヘッダーの書き込み順序が保証されておらず、Proc-TypeヘッダーがRFC 1421の要件を満たさない位置に書き出される可能性がありました。これにより、一部の厳格なPEMパーサーとの互換性の問題が発生する可能性がありました。このコミットは、この仕様不適合を修正し、生成されるPEMブロックの堅牢性を高めることを目的としています。

前提知識の解説

PEM (Privacy-Enhanced Mail) 形式

PEMは、バイナリデータをASCIIテキスト形式に変換するためのエンコード方式です。主に、X.509証明書、RSA秘密鍵、DHパラメータなどの暗号関連データを表現するために使用されます。PEM形式の構造は以下のようになります。

-----BEGIN <Type>-----
Header1: Value1
Header2: Value2

Base64 encoded data
-----END <Type>-----
  • 境界線: -----BEGIN <Type>----------END <Type>-----でデータの種類(例: RSA PRIVATE KEY, CERTIFICATE)を示します。
  • ヘッダー: オプションで、データに関する追加情報(例: 暗号化の種類、鍵のバージョン)をKey: Value形式で記述します。ヘッダーとBase64エンコードされたデータの間には空行が必要です。
  • Base64エンコードデータ: 実際のバイナリデータがBase64でエンコードされて格納されます。

RFC 1421

RFC 1421は「Privacy Enhancement for Internet Electronic Mail: Part I: Message Encryption and Authentication Procedures」というタイトルで、インターネット電子メールのプライバシー強化のための初期の仕様です。このRFCは、電子メールメッセージの暗号化と認証の手順を定義しており、その中でPEM形式の構造についても詳細に記述しています。

RFC 1421, Section 4.6.1.1: Proc-Type Field

このセクションは、PEMヘッダーの一つであるProc-Typeフィールドについて具体的に説明しています。Proc-Typeは「Processing Type」の略で、PEMブロックの内容がどのように処理されるべきかを示すために使用されます。例えば、Proc-Type: 4,ENCRYPTEDは、そのPEMブロックが暗号化されたデータを含んでいることを示します。

RFC 1421のこのセクションでは、Proc-Typeヘッダーが他のヘッダーに先立って記述されるべきであるという要件が示されています。これは、PEMパーサーが最初にこのヘッダーを読み取ることで、後続のデータの処理方法を迅速に判断できるようにするためです。

技術的詳細

このコミットの主要な変更点は、encoding/pemパッケージのEncode関数におけるヘッダーの書き込みロジックです。以前の実装では、b.Headersマップをイテレートしてヘッダーを書き込んでいましたが、Goのマップのイテレーション順序は保証されていないため、Proc-Typeヘッダーが常に最初に書き込まれるとは限りませんでした。

この修正では、以下の手順でヘッダーを書き込むように変更されています。

  1. b.HeadersマップからProc-Typeヘッダーが存在するかどうかを確認します。
  2. もしProc-Typeヘッダーが存在する場合、RFC 1421の要件に従い、最初にこのヘッダーを書き込みます。
  3. 残りのヘッダー(Proc-Type以外のヘッダー)は、出力の一貫性を保つためにキーでソートされた順序で書き込まれます。これにより、同じヘッダーを持つPEMブロックは常に同じ順序でヘッダーが出力されるようになります。

この変更により、encoding/pemパッケージによって生成されるPEMブロックは、RFC 1421のProc-Typeヘッダーの順序に関する要件に厳密に準拠するようになります。

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

このコミットで変更された主要なファイルは以下の2つです。

  1. src/pkg/encoding/pem/pem.go: PEMエンコードのロジックが含まれる主要なファイル。Encode関数のヘッダー書き込み部分が修正されました。
  2. src/pkg/encoding/pem/pem_test.go: encoding/pemパッケージのテストファイル。新しいテストケースが追加され、Proc-Typeヘッダーを含むPEMブロックのエンコードとデコードが正しく行われることを検証しています。特に、privateKey2の定義が変更され、Proc-Typeヘッダーが追加されています。

src/pkg/encoding/pem/pem.go の変更点

  • sortパッケージがインポートに追加されました。
  • writeHeaderというヘルパー関数が追加され、ヘッダーの書き込みエラーハンドリングを簡素化しました。
  • Encode関数内で、ヘッダーの処理ロジックが大幅に変更されました。
    • Proc-Typeヘッダーを特別扱いし、最初に書き込むロジックが追加されました。
    • それ以外のヘッダーは、キーをスライスに集めてソートし、その順序で書き出すように変更されました。

src/pkg/encoding/pem/pem_test.go の変更点

  • TestDecodeTestEncode関数内で使用されるテストデータpemPrivateKeypemPrivateKey2に変更されました。
  • privateKey2の定義が更新され、Proc-Type, Content-Domain, DEK-Infoなどのヘッダーが追加されました。これにより、ヘッダーを含むPEMブロックのテストが強化されました。
  • 対応するpemPrivateKey2の文字列リテラルも更新され、新しいヘッダーと、それによって変更されたBase64エンコードデータが反映されています。

コアとなるコードの解説

src/pkg/encoding/pem/pem.go の変更点詳細

// func Encode(out io.Writer, b *Block) error { ... } 内の変更

 	if len(b.Headers) > 0 {
 		const procType = "Proc-Type"
 		h := make([]string, 0, len(b.Headers))
 		hasProcType := false
 		for k := range b.Headers {
 			if k == procType {
 				hasProcType = true
 				continue
 			}
 			h = append(h, k)
 		}
 		// The Proc-Type header must be written first.
 		// See RFC 1421, section 4.6.1.1
 		if hasProcType {
 			if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
 				return err
 			}
 		}
 		// For consistency of output, write other headers sorted by key.
 		sort.Strings(h)
 		for _, k := range h {
 			if err := writeHeader(out, k, b.Headers[k]); err != nil {
 				return err
 			}
 		}
 		if _, err := out.Write([]byte{'\n'}); err != nil {
 			return err
 		}
 	}

このコードブロックが、ヘッダーの書き込み順序を制御する核心部分です。

  1. procType定数の定義: Proc-Typeヘッダーのキーを定数として定義しています。
  2. ヘッダーの分離:
    • hという文字列スライスを作成し、Proc-Type以外のすべてのヘッダーキーを格納します。
    • hasProcTypeブール変数でProc-Typeヘッダーが存在するかどうかを追跡します。
  3. Proc-Typeの優先書き込み:
    • if hasProcTypeブロック内で、もしProc-Typeヘッダーが存在すれば、writeHeaderヘルパー関数を使って最初にこのヘッダーを書き込みます。これはRFC 1421の要件を満たすためです。
  4. その他のヘッダーのソートと書き込み:
    • sort.Strings(h)によって、Proc-Type以外のヘッダーキーがアルファベット順にソートされます。
    • ソートされたキーの順序で、残りのヘッダーがwriteHeader関数を使って書き込まれます。これにより、出力の一貫性が保たれます。
  5. 空行の書き込み: すべてのヘッダーが書き込まれた後、ヘッダーとBase64エンコードデータの間を区切るための空行が書き込まれます。

このロジックにより、Proc-Typeヘッダーは常に他のヘッダーよりも前に出力され、かつ他のヘッダーは決定論的な順序(キーのアルファベット順)で出力されるようになります。

関連リンク

参考にした情報源リンク