[インデックス 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
ヘッダーが常に最初に書き込まれるとは限りませんでした。
この修正では、以下の手順でヘッダーを書き込むように変更されています。
b.Headers
マップからProc-Type
ヘッダーが存在するかどうかを確認します。- もし
Proc-Type
ヘッダーが存在する場合、RFC 1421の要件に従い、最初にこのヘッダーを書き込みます。 - 残りのヘッダー(
Proc-Type
以外のヘッダー)は、出力の一貫性を保つためにキーでソートされた順序で書き込まれます。これにより、同じヘッダーを持つPEMブロックは常に同じ順序でヘッダーが出力されるようになります。
この変更により、encoding/pem
パッケージによって生成されるPEMブロックは、RFC 1421のProc-Type
ヘッダーの順序に関する要件に厳密に準拠するようになります。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルは以下の2つです。
src/pkg/encoding/pem/pem.go
: PEMエンコードのロジックが含まれる主要なファイル。Encode
関数のヘッダー書き込み部分が修正されました。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
の変更点
TestDecode
とTestEncode
関数内で使用されるテストデータpemPrivateKey
がpemPrivateKey2
に変更されました。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
}
}
このコードブロックが、ヘッダーの書き込み順序を制御する核心部分です。
procType
定数の定義:Proc-Type
ヘッダーのキーを定数として定義しています。- ヘッダーの分離:
h
という文字列スライスを作成し、Proc-Type
以外のすべてのヘッダーキーを格納します。hasProcType
ブール変数でProc-Type
ヘッダーが存在するかどうかを追跡します。
Proc-Type
の優先書き込み:if hasProcType
ブロック内で、もしProc-Type
ヘッダーが存在すれば、writeHeader
ヘルパー関数を使って最初にこのヘッダーを書き込みます。これはRFC 1421の要件を満たすためです。
- その他のヘッダーのソートと書き込み:
sort.Strings(h)
によって、Proc-Type
以外のヘッダーキーがアルファベット順にソートされます。- ソートされたキーの順序で、残りのヘッダーが
writeHeader
関数を使って書き込まれます。これにより、出力の一貫性が保たれます。
- 空行の書き込み: すべてのヘッダーが書き込まれた後、ヘッダーとBase64エンコードデータの間を区切るための空行が書き込まれます。
このロジックにより、Proc-Type
ヘッダーは常に他のヘッダーよりも前に出力され、かつ他のヘッダーは決定論的な順序(キーのアルファベット順)で出力されるようになります。
関連リンク
- Go CL (Code Review) ページ: https://golang.org/cl/6814104
参考にした情報源リンク
- RFC 1421 - Privacy Enhancement for Internet Electronic Mail: Part I: Message Encryption and Authentication Procedures:
- https://www.rfc-editor.org/rfc/rfc1421
- 特にセクション 4.6.1.1 (Proc-Type Field) を参照。
- freesoft.org (RFC 1421, Section 4.6.1.1): https://www.freesoft.org/CIE/RFC/1421/19.htm
- ietf.org (RFC 1421, Section 4.6.1.1.3): https://www.ietf.org/rfc/rfc1421.txt (MIC-CLEAR messagesに関する記述)