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

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

このコミットは、Go言語の標準ライブラリ crypto/x509 パッケージにおいて、crypto/sha256 パッケージを明示的にインポートするように変更するものです。これにより、SHA-256がデフォルトのハッシュ関数として使用されるようになったGoの環境において、x509 を利用するプログラムがハッシュ関数がリンクされていないことによるエラーで失敗するのを防ぎます。

コミット

commit 9144ef332191f6a85d16d261ccaea9a998750aea
Author: Adam Langley <agl@golang.org>
Date:   Thu Dec 19 14:06:28 2013 -0500

    crypto/x509: import crypto/sha256
    
    Since SHA-256 is now the default hash function, x509 should import it
    otherwise some programs may fail because it hasn't been linked in.
    
    R=golang-dev, dave, minux.ma
    CC=golang-dev
    https://golang.org/cl/44010047

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

https://github.com/golang/go/commit/9144ef332191f6a85d16d261ccaea9a998750aea

元コミット内容

crypto/x509: import crypto/sha256

SHA-256がデフォルトのハッシュ関数になったため、x509はそれをインポートすべきである。そうしないと、一部のプログラムはリンクされていないために失敗する可能性がある。

変更の背景

この変更の背景には、Go言語の暗号ライブラリにおけるハッシュ関数のデフォルト設定の進化があります。Go 1.1以降、特にTLS (Transport Layer Security) などのプロトコルにおいて、より強力な暗号学的ハッシュ関数であるSHA-256が推奨され、デフォルトとして扱われる傾向が強まりました。

Goのビルドシステムとリンカの特性上、あるパッケージが別のパッケージの機能を利用する場合、そのパッケージを明示的にインポートする必要があります。しかし、crypto/sha256 のようなハッシュ関数パッケージは、通常、そのパッケージ内の関数を直接呼び出すのではなく、crypto.Hash インターフェースを介して間接的に利用されることが多いです。この場合、crypto/x509 が証明書の署名などにSHA-256を使用しようとしても、crypto/sha256 パッケージがどこからも直接インポートされていないと、リンカがそのコードを含めない可能性があります。

結果として、crypto/x509 を利用するアプリケーションが、SHA-256を必要とする操作(例えば、SHA-256で署名された証明書の検証や生成)を実行しようとした際に、「ハッシュ関数が登録されていない」といった実行時エラーや、単に機能が利用できないといった問題が発生する可能性がありました。このコミットは、このような潜在的な問題を回避し、crypto/x509 が常にSHA-256の機能を利用できるようにするために行われました。

前提知識の解説

Go言語のパッケージインポートとリンカの挙動

Go言語では、import ステートメントを使用して他のパッケージの機能を利用します。通常、インポートされたパッケージの公開された関数や変数にアクセスするためにインポートしますが、Goには「ブランクインポート(blank import)」という特殊な形式があります。

import _ "package/path"

この形式でインポートされたパッケージは、そのパッケージ内の init() 関数を実行するためにのみインポートされます。init() 関数は、パッケージが初期化される際に自動的に実行される特殊な関数で、グローバル変数の初期化や、パッケージ固有のセットアップ処理(今回のケースのように、ハッシュ関数をシステムに登録するなど)によく使用されます。ブランクインポートは、パッケージの副作用を利用したいが、そのパッケージの識別子を直接使用しない場合に用いられます。

Goのリンカは、実際に使用されるコードのみを最終的なバイナリに含めるように最適化されています。もしあるパッケージがどこからも参照されず、その init() 関数も実行される必要がないと判断された場合、リンカはそのパッケージのコードをバイナリから除外する可能性があります。

crypto/x509 と証明書署名

crypto/x509 パッケージは、X.509証明書とCRL (Certificate Revocation List) の解析、生成、検証を行うためのGoの標準ライブラリです。デジタル証明書は、公開鍵暗号の公開鍵と、その公開鍵の所有者に関する情報を結びつけるもので、認証や暗号化通信の基盤となります。

証明書の署名プロセスでは、証明書の内容(公開鍵、所有者情報、有効期限など)をハッシュ関数で要約し、そのハッシュ値を秘密鍵で暗号化(署名)します。署名された証明書を受け取った側は、対応する公開鍵とハッシュ関数を使って署名を検証し、証明書の完全性と信頼性を確認します。

ハッシュ関数の登録

Goの crypto パッケージ群では、様々な暗号学的ハッシュ関数(SHA-1, SHA-256, SHA-512など)が提供されています。これらのハッシュ関数は、crypto.Hash インターフェースを実装しており、crypto.RegisterHash 関数を通じてシステムに登録されます。これにより、crypto/x509 のような高レベルの暗号パッケージが、具体的なハッシュ関数の実装を知らなくても、登録されたハッシュ関数を名前やIDで参照して利用できるようになります。

このコミット以前は、crypto/sha256 パッケージが直接インポートされていない場合、その init() 関数が実行されず、SHA-256ハッシュ関数がシステムに登録されない可能性がありました。その結果、crypto/x509 がSHA-256を必要としたときに、利用可能なハッシュ関数として見つけられず、エラーとなることがあったのです。

技術的詳細

このコミットの技術的詳細な変更点は以下の通りです。

  1. crypto/x509/x509.go へのブランクインポートの追加: src/pkg/crypto/x509/x509.go ファイルに _ "crypto/sha256" という行が追加されました。これは、crypto/sha256 パッケージをブランクインポートするものです。このインポートにより、crypto/sha256 パッケージの init() 関数が必ず実行されるようになります。crypto/sha256 パッケージの init() 関数は、SHA-256ハッシュ関数をGoの暗号システムに登録する役割を担っています。これにより、crypto/x509 がSHA-256を必要とする際に、確実に利用可能になります。

  2. テストケース x509_test_import.go の追加: 新しいテストファイル src/pkg/crypto/x509/x509_test_import.go が追加されました。このファイルは、+build ignore ディレクティブを持つ独立したGoプログラムです。これは、通常のGoビルドプロセスではコンパイルされないことを意味します。 このテストプログラムの目的は、crypto/x509 を使用して証明書を生成する際に、必要最低限のインポートしか行わないプログラムでも、ハッシュ関数が不足することなく証明書署名が成功するかどうかを検証することです。具体的には、crypto/randcrypto/x509crypto/x509/pkixencoding/pemmath/bigtime のみインポートし、x509.CreateCertificate を呼び出しています。このテストが成功すれば、crypto/x509 が内部的に crypto/sha256 を適切にロードしていることが保証されます。

  3. x509_test.go からのテスト実行: src/pkg/crypto/x509/x509_test.go ファイルに TestImports という新しいテスト関数が追加されました。この関数は、os/exec パッケージを使用して、先ほど説明した x509_test_import.go プログラムを外部プロセスとして実行します。もし x509_test_import.go の実行が失敗した場合(例えば、ハッシュ関数が登録されていないために証明書生成が失敗した場合)、TestImports テストも失敗します。これにより、将来的に同様のリンキング問題が発生しないことを継続的に保証できます。

これらの変更は、Goのビルドシステムと暗号ライブラリの相互作用における微妙な側面に対処しています。ブランクインポートは、Goのリンカが未使用と判断してコードを削除してしまうことを防ぎ、必要な副作用(この場合はハッシュ関数の登録)が確実に発生するようにします。新しいテストは、この保証が将来の変更によって損なわれないようにするためのセーフティネットとして機能します。

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

src/pkg/crypto/x509/x509.go

--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -13,6 +13,7 @@ import (
 	"crypto/elliptic"
 	"crypto/rsa"
 	"crypto/sha1"
+	_ "crypto/sha256"
 	"crypto/x509/pkix"
 	"encoding/asn1"
 	"encoding/pem"

src/pkg/crypto/x509/x509_test.go

--- a/src/pkg/crypto/x509/x509_test.go
+++ b/src/pkg/crypto/x509/x509_test.go
@@ -20,6 +20,7 @@ import (
 	"encoding/pem"
 	"math/big"
 	"net"
+	"os/exec"
 	"reflect"
 	"testing"
 	"time"
@@ -725,6 +726,12 @@ func TestParsePEMCRL(t *testing.T) {
 	// Can't check the signature here without a package cycle.
 }
 
+func TestImports(t *testing.T) {
+	if err := exec.Command("go", "run", "x509_test_import.go").Run(); err != nil {
+		t.Errorf("failed to run x509_test_import.go: %s", err)
+	}
+}
+
 const derCRLBase64 = "MIINqzCCDJMCAQEwDQYJKoZIhvcNAQEFBQAwVjEZMBcGA1UEAxMQUEtJIEZJTk1FQ0NBTklDQTEVMBMGA1UEChMMRklOTUVDQ0FOSUNBMRUwEwYDVQQLEwxGSU5NRUNDQW5JQ0ExCzAJBgNVBAYTAklUFw0xMTA1MDQxNjU3NDJaFw0xMTA1MDQyMDU3NDJaMIIMBzAhAg4Ze1od49Lt1qIXBydAzhcNMDkwNzE2MDg0MzIyWjAAMCECDl0HSL9bcZ1Ci/UHJ0DPFw0wOTA3MTYwODQzMTNaMAAwIQIOESB9tVAmX3cY7QcnQNAXDTA5MDcxNjA4NDUyMlowADAhAg4S1tGAQ3mHt8uVBydA1RcNMDkwODA0MTUyNTIyWjAAMCECDlQ249Y7vtC25ScHJ0DWFw0wOTA4MDQxNTI1MzdaMAAwIQIOISMop3NkA4PfYwcnQNkXDTA5MDgwNDExMDAzNFowADAhAg56/BMoS29KEShTBydA2hcNMDkwODA0MTEwMTAzWjAAMCECDnBp/22HPH5CSWoHJ0DbFw0wOTA4MDQxMDU0NDlaMAAwIQIOV9IP+8CD8bK+XAcnQNwXDTA5MDgwNDEwNTcxN1owADAhAg4v5aRz0IxWqYiXBydA3RcNMDkwODA0MTA1NzQ1WjAAMCECDlOU34VzvZAybQwHJ0DeFw0wOTA4MDQxMDU4MjFaMAAwIAINO4CD9lluIxcwBydBAxcNMDkwNzIyMTUzMTU5WjAAMCECDgOllfO8Y1QA7/wHJ0ExFw0wOTA3MjQxMTQxNDNaMAAwIQIOJBX7TbiCdRdyjgcnQUQXDTA5MDkxNjA5MzAwOFowADAhAg5iYSAgmDrlH/RZBydBRRcNMDkwOTE2MDkzMDE3WjAAMCECDmu6k6srP3jcMaQHJ0FRFw0wOTA4MDQxMDU2NDBaMAAwIQIOX8aHlO0V+WVH4QcnQVMXDTA5MDgwNDEwNTcyOVowADAhAg5flK2rg3NnsRgDBydBzhcNMTEwMjAxMTUzMzQ2WjAAMCECDg35yJDL1jOPTgoHJ0HPFw0xMTAyMDExNTM0MjZaMAAwIQIOMyFJ6+e9iiGVBQcnQdAXDTA5MDkxODEzMjAwNVowADAhAg5Emb/Oykucmn8fBydB1xcNMDkwOTIxMTAxMDQ3WjAAMCECDjQKCncV+MnUavMHJ0HaFw0wOTA5MjIwODE1MjZaMAAwIQIOaxiFUt3dpd+tPwcnQfQXDTEwMDYxODA4NDI1MVowADAhAg5G7P8nO0tkrMt7BydB9RcNMTAwNjE4MDg0MjMwWjAAMCECDmTCC3SXhmDRst4HJ0H2Fw0wOTA5MjgxMjA3MjBaMAAwIQIOHoGhUr/pRwzTKgcnQfcXDTA5MDkyODEyMDcyNFowADAhAg50wrcrCiw8mQmPBydCBBcNMTAwMjE2MTMwMTA2WjAAMCECDifWmkvwyhEqwEcHJ0IFFw0xMDAyMTYxMzAxMjBaMAAwIQIOfgPmlW9fg+osNgcnQhwXDTEwMDQxMzA5NTIwMFowADAhAg4YHAGuA6LgCk7tBydCHRcNMTAwNDEzMDk1MTM4WjAAMCECDi1zH1bxkNJhokAHJ0IsFw0xMDA0MTMwOTU5MzBaMAAwIQIOMipNccsb/wo2fwcnQi0XDTEwMDQxMzA5NTkwMFowADAhAg46lCmvPl4GpP6ABydCShcNMTAwMTE5MDk1MjE3WjAAMCECDjaTcaj+wBpcGAsHJ0JLFw0xMDAxMTkwOTUyMzRaMAAwIQIOOMC13EOrBuxIOQcnQloXDTEwMDIwMTA5NDcwNVowADAhAg5KmZl+krz4RsmrBydCWxcNMTAwMjAxMDk0NjQwWjAAMCECDmLG3zQJ/fzdSsUHJ0JiFw0xMDAzMDEwOTUxNDBaMAAwIQIOP39ksgHdojf4owcnQmMXDTEwMDMwMTA5NTExN1owADAhAg4LD... [truncated]

src/pkg/crypto/x509/x509_test_import.go (新規ファイル)

--- /dev/null
+++ b/src/pkg/crypto/x509/x509_test_import.go
@@ -0,0 +1,53 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// This file is run by the x509 tests to ensure that a program with minimal
+// imports can sign certificates without errors resulting from missing hash
+// functions.
+package main
+
+import (
+	"crypto/rand"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+	"math/big"
+	"time"
+)
+
+func main() {
+	block, _ := pem.Decode([]byte(pemPrivateKey))
+	rsaPriv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
+	if err != nil {
+		panic("Failed to parse private key: " + err.Error())
+	}
+
+	template := x509.Certificate{
+		SerialNumber: big.NewInt(1),
+		Subject: pkix.Name{
+			CommonName:   "test",
+			Organization: []string{"Σ Acme Co"},
+		},
+		NotBefore: time.Unix(1000, 0),
+		NotAfter:  time.Unix(100000, 0),
+		KeyUsage:  x509.KeyUsageCertSign,
+	}
+
+	if _, err = x509.CreateCertificate(rand.Reader, &template, &template, &rsaPriv.PublicKey, rsaPriv); err != nil {
+		panic("failed to create certificate with basic imports: " + err.Error())
+	}
+}
+
+var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
+MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
+fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
+/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
+RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
+EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
+IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
+tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
+-----END RSA PRIVATE KEY-----
+`

コアとなるコードの解説

src/pkg/crypto/x509/x509.go の変更

import _ "crypto/sha256" の追加は、このコミットの最も重要な変更点です。前述の通り、これはブランクインポートであり、crypto/sha256 パッケージの init() 関数が確実に実行されるようにします。crypto/sha256 パッケージの init() 関数は、SHA-256ハッシュアルゴリズムをGoの暗号システムに登録します。これにより、crypto/x509 パッケージが証明書の署名や検証を行う際に、SHA-256ハッシュ関数が常に利用可能な状態になります。

この変更がなければ、crypto/sha256 を直接インポートしていないアプリケーションが crypto/x509 を使用した場合、SHA-256が必要な場面で「ハッシュ関数が見つからない」といったエラーが発生する可能性がありました。これは、Goのリンカが、直接参照されていないコードを最終バイナリから除外する最適化を行うためです。ブランクインポートは、この最適化を回避し、必要なコードが確実に含まれるようにするためのGoのイディオムです。

src/pkg/crypto/x509/x509_test.go および src/pkg/crypto/x509/x509_test_import.go の変更

x509_test_import.go は、crypto/x509 が最小限のインポートで証明書を生成できることを検証するための独立したテストプログラムです。このプログラムは、+build ignore ディレクティブによって通常のビルドから除外されており、x509_test.go から os/exec.Command("go", "run", "x509_test_import.go").Run() を使って外部プロセスとして実行されます。

このテストの目的は、crypto/x509 が内部的に必要なハッシュ関数(この場合はSHA-256)を適切にロードし、利用できることを保証することです。もし crypto/sha256 のブランクインポートがなければ、このテストはハッシュ関数が見つからないために失敗するでしょう。このテストの追加により、将来のGoの変更やリンカの挙動の変化によって、同様の問題が再発するのを防ぐための堅牢なチェックが導入されました。

pemPrivateKey 変数に含まれるRSA秘密鍵は、テスト用のダミーデータであり、実際のセキュリティには影響しません。これは、テスト環境で証明書署名操作をシミュレートするために使用されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード (特に crypto および crypto/x509 パッケージ)
  • Go言語のコミット履歴とGerritレビューシステム (CL 44010047)
  • Go言語のビルドシステムとリンカに関する一般的な情報
  • X.509証明書とデジタル署名に関する一般的な暗号学の知識# [インデックス 18084] ファイルの概要

このコミットは、Go言語の標準ライブラリ crypto/x509 パッケージにおいて、crypto/sha256 パッケージを明示的にインポートするように変更するものです。これにより、SHA-256がデフォルトのハッシュ関数として使用されるようになったGoの環境において、x509 を利用するプログラムがハッシュ関数がリンクされていないことによるエラーで失敗するのを防ぎます。

コミット

commit 9144ef332191f6a85d16d261ccaea9a998750aea
Author: Adam Langley <agl@golang.org>
Date:   Thu Dec 19 14:06:28 2013 -0500

    crypto/x509: import crypto/sha256
    
    Since SHA-256 is now the default hash function, x509 should import it
    otherwise some programs may fail because it hasn't been linked in.
    
    R=golang-dev, dave, minux.ma
    CC=golang-dev
    https://golang.org/cl/44010047

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

https://github.com/golang/go/commit/9144ef332191f6a85d16d261ccaea9a998750aea

元コミット内容

crypto/x509: import crypto/sha256

SHA-256がデフォルトのハッシュ関数になったため、x509はそれをインポートすべきである。そうしないと、一部のプログラムはリンクされていないために失敗する可能性がある。

変更の背景

この変更の背景には、Go言語の暗号ライブラリにおけるハッシュ関数のデフォルト設定の進化があります。Go 1.1以降、特にTLS (Transport Layer Security) などのプロトコルにおいて、より強力な暗号学的ハッシュ関数であるSHA-256が推奨され、デフォルトとして扱われる傾向が強まりました。

Goのビルドシステムとリンカの特性上、あるパッケージが別のパッケージの機能を利用する場合、そのパッケージを明示的にインポートする必要があります。しかし、crypto/sha256 のようなハッシュ関数パッケージは、通常、そのパッケージ内の関数を直接呼び出すのではなく、crypto.Hash インターフェースを介して間接的に利用されることが多いです。この場合、crypto/x509 が証明書の署名などにSHA-256を使用しようとしても、crypto/sha256 パッケージがどこからも直接インポートされていないと、リンカがそのコードを含めない可能性があります。

結果として、crypto/x509 を利用するアプリケーションが、SHA-256を必要とする操作(例えば、SHA-256で署名された証明書の検証や生成)を実行しようとした際に、「ハッシュ関数が登録されていない」といった実行時エラーや、単に機能が利用できないといった問題が発生する可能性がありました。このコミットは、このような潜在的な問題を回避し、crypto/x509 が常にSHA-256の機能を利用できるようにするために行われました。

前提知識の解説

Go言語のパッケージインポートとリンカの挙動

Go言語では、import ステートメントを使用して他のパッケージの機能を利用します。通常、インポートされたパッケージの公開された関数や変数にアクセスするためにインポートしますが、Goには「ブランクインポート(blank import)」という特殊な形式があります。

import _ "package/path"

この形式でインポートされたパッケージは、そのパッケージ内の init() 関数を実行するためにのみインポートされます。init() 関数は、パッケージが初期化される際に自動的に実行される特殊な関数で、グローバル変数の初期化や、パッケージ固有のセットアップ処理(今回のケースのように、ハッシュ関数をシステムに登録するなど)によく使用されます。ブランクインポートは、パッケージの副作用を利用したいが、そのパッケージの識別子を直接使用しない場合に用いられます。

Goのリンカは、実際に使用されるコードのみを最終的なバイナリに含めるように最適化されています。もしあるパッケージがどこからも参照されず、その init() 関数も実行される必要がないと判断された場合、リンカはそのパッケージのコードをバイナリから除外する可能性があります。

crypto/x509 と証明書署名

crypto/x509 パッケージは、X.509証明書とCRL (Certificate Revocation List) の解析、生成、検証を行うためのGoの標準ライブラリです。デジタル証明書は、公開鍵暗号の公開鍵と、その公開鍵の所有者に関する情報を結びつけるもので、認証や暗号化通信の基盤となります。

証明書の署名プロセスでは、証明書の内容(公開鍵、所有者情報、有効期限など)をハッシュ関数で要約し、そのハッシュ値を秘密鍵で暗号化(署名)します。署名された証明書を受け取った側は、対応する公開鍵とハッシュ関数を使って署名を検証し、証明書の完全性と信頼性を確認します。

ハッシュ関数の登録

Goの crypto パッケージ群では、様々な暗号学的ハッシュ関数(SHA-1, SHA-256, SHA-512など)が提供されています。これらのハッシュ関数は、crypto.Hash インターフェースを実装しており、crypto.RegisterHash 関数を通じてシステムに登録されます。これにより、crypto/x509 のような高レベルの暗号パッケージが、具体的なハッシュ関数の実装を知らなくても、登録されたハッシュ関数を名前やIDで参照して利用できるようになります。

このコミット以前は、crypto/sha256 パッケージが直接インポートされていない場合、その init() 関数が実行されず、SHA-256ハッシュ関数がシステムに登録されない可能性がありました。その結果、crypto/x509 がSHA-256を必要としたときに、利用可能なハッシュ関数として見つけられず、エラーとなることがあったのです。

技術的詳細

このコミットの技術的詳細な変更点は以下の通りです。

  1. crypto/x509/x509.go へのブランクインポートの追加: src/pkg/crypto/x509/x509.go ファイルに _ "crypto/sha256" という行が追加されました。これは、crypto/sha256 パッケージをブランクインポートするものです。このインポートにより、crypto/sha256 パッケージの init() 関数が必ず実行されるようになります。crypto/sha256 パッケージの init() 関数は、SHA-256ハッシュ関数をGoの暗号システムに登録する役割を担っています。これにより、crypto/x509 がSHA-256を必要とする際に、確実に利用可能になります。

  2. テストケース x509_test_import.go の追加: 新しいテストファイル src/pkg/crypto/x509/x509_test_import.go が追加されました。このファイルは、+build ignore ディレクティブを持つ独立したGoプログラムです。これは、通常のGoビルドプロセスではコンパイルされないことを意味します。 このテストプログラムの目的は、crypto/x509 を使用して証明書を生成する際に、必要最低限のインポートしか行わないプログラムでも、ハッシュ関数が不足することなく証明書署名が成功するかどうかを検証することです。具体的には、crypto/randcrypto/x509crypto/x509/pkixencoding/pemmath/bigtime のみインポートし、x509.CreateCertificate を呼び出しています。このテストが成功すれば、crypto/x509 が内部的に crypto/sha256 を適切にロードしていることが保証されます。

  3. x509_test.go からのテスト実行: src/pkg/crypto/x509/x509_test.go ファイルに TestImports という新しいテスト関数が追加されました。この関数は、os/exec パッケージを使用して、先ほど説明した x509_test_import.go プログラムを外部プロセスとして実行します。もし x509_test_import.go の実行が失敗した場合(例えば、ハッシュ関数が登録されていないために証明書生成が失敗した場合)、TestImports テストも失敗します。これにより、将来的に同様のリンキング問題が発生しないことを継続的に保証できます。

これらの変更は、Goのビルドシステムと暗号ライブラリの相互作用における微妙な側面に対処しています。ブランクインポートは、Goのリンカが未使用と判断してコードを削除してしまうことを防ぎ、必要な副作用(この場合はハッシュ関数の登録)が確実に発生するようにします。新しいテストは、この保証が将来の変更によって損なわれないようにするためのセーフティネットとして機能します。

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

src/pkg/crypto/x509/x509.go

--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -13,6 +13,7 @@ import (
 	"crypto/elliptic"
 	"crypto/rsa"
 	"crypto/sha1"
+	_ "crypto/sha256"
 	"crypto/x509/pkix"
 	"encoding/asn1"
 	"encoding/pem"

src/pkg/crypto/x509/x509_test.go

--- a/src/pkg/crypto/x509/x509_test.go
+++ b/src/pkg/crypto/x509/x509_test.go
@@ -20,6 +20,7 @@ import (
 	"encoding/pem"
 	"math/big"
 	"net"
+	"os/exec"
 	"reflect"
 	"testing"
 	"time"
@@ -725,6 +726,12 @@ func TestParsePEMCRL(t *testing.T) {
 	// Can't check the signature here without a package cycle.
 }
 
+func TestImports(t *testing.T) {
+	if err := exec.Command("go", "run", "x509_test_import.go").Run(); err != nil {
+		t.Errorf("failed to run x509_test_import.go: %s", err)
+	}
+}
+
 const derCRLBase64 = "MIINqzCCDJMCAQEwDQYJKoZIhvcNAQEFBQAwVjEZMBcGA1UEAxMQUEtJIEZJTk1FQ0NBTklDQTEVMBMGA1UEChMMRklOTUVDQ0FOSUNBMRUwEwYDVQQLEwxGSU5NRUNDQW5JQ0ExCzAJBgNVBAYTAklUFw0xMTA1MDQxNjU3NDJaFw0xMTA1MDQyMDU3NDJaMIIMBzAhAg4Ze1od49Lt1qIXBydAzhcNMDkwNzE2MDg0MzIyWjAAMCECDl0HSL9bcZ1Ci/UHJ0DPFw0wOTA3MTYwODQzMTNaMAAwIQIOESB9tVAmX3cY7QcnQNAXDTA5MDcxNjA4NDUyMlowADAhAg4S1tGAQ3mHt8uVBydA1RcNMDkwODA0MTUyNTIyWjAAMCECDlQ249Y7vtC25ScHJ0DWFw0wOTA4MDQxNTI1MzdaMAAwIQIOISMop3NkA4PfYwcnQNkXDTA5MDgwNDExMDAzNFowADAhAg56/BMoS29KEShTBydA2hcNMDkwODA0MTEwMTAzWjAAMCECDnBp/22HPH5CSWoHJ0DbFw0wOTA4MDQxMDU0NDlaMAAwIQIOV9IP+8CD8bK+XAcnQNwXDTA5MDgwNDEwNTcxN1owADAhAg4v5aRz0IxWqYiXBydA3RcNMDkwODA0MTA1NzQ1WjAAMCECDlOU34VzvZAybQwHJ0DeFw0wOTA4MDQxMDU4MjFaMAAwIAINO4CD9lluIxcwBydBAxcNMDkwNzIyMTUzMTU5WjAAMCECDgOllfO8Y1QA7/wHJ0ExFw0wOTA3MjQxMTQxNDNaMAAwIQIOJBX7TbiCdRdyjgcnQUQXDTA5MDkxNjA5MzAwOFowADAhAg5iYSAgmDrlH/RZBydBRRcNMDkwOTE2MDkzMDE3WjAAMCECDmu6k6srP3jcMaQHJ0FRFw0wOTA4MDQxMDU2NDBaMAAwIQIOX8aHlO0V+WVH4QcnQVMXDTA5MDgwNDEwNTcyOVowADAhAg5flK2rg3NnsRgDBydBzhcNMTEwMjAxMTUzMzQ2WjAAMCECDg35yJDL1jOPTgoHJ0HPFw0xMTAyMDExNTM0MjZaMAAwIQIOMyFJ6+e9iiGVBQcnQdAXDTA5MDkxODEzMjAwNVowADAhAg5Emb/Oykucmn8fBydB1xcNMDkwOTIxMTAxMDQ3WjAAMCECDjQKCncV+MnUavMHJ0HaFw0wOTA5MjIwODE1MjZaMAAwIQIOaxiFUt3dpd+tPwcnQfQXDTEwMDYxODA4NDI1MVowADAhAg5G7P8nO0tkrMt7BydB9RcNMTAwNjE4MDg0MjMwWjAAMCECDmTCC3SXhmDRst4HJ0H2Fw0wOTA5MjgxMjA3MjBaMAAwIQIOHoGhUr/pRwzTKgcnQfcXDTA5MDkyODEyMDcyNFowADAhAg50wrcrCiw8mQmPBydCBBcNMTAwMjE2MTMwMTA2WjAAMCECDifWmkvwyhEqwEcHJ0IFFw0xMDAyMTYxMzAxMjBaMAAwIQIOfgPmlW9fg+osNgcnQhwXDTEwMDQxMzA5NTIwMFowADAhAg4YHAGuA6LgCk7tBydCHRcNMTAwNDEzMDk1MTM4WjAAMCECDi1zH1bxkNJhokAHJ0IsFw0xMDA0MTMwOTU5MzBaMAAwIQIOMipNccsb/wo2fwcnQi0XDTEwMDQxMzA5NTkwMFowADAhAg46lCmvPl4GpP6ABydCShcNMTAwMTE5MDk1MjE3WjAAMCECDjaTcaj+wBpcGAsHJ0JLFw0xMDAxMTkwOTUyMzRaMAAwIQIOOMC13EOrBuxIOQcnQloXDTEwMDIwMTA5NDcwNVowADAhAg5KmZl+krz4RsmrBydCWxcNMTAwMjAxMDk0NjQwWjAAMCECDmLG3zQJ/fzdSsUHJ0JiFw0xMDAzMDEwOTUxNDBaMAAwIQIOP39ksgHdojf4owcnQmMXDTEwMDMwMTA5NTExN1owADAhAg4LD... [truncated]

src/pkg/crypto/x509/x509_test_import.go (新規ファイル)

--- /dev/null
+++ b/src/pkg/crypto/x509/x509_test_import.go
@@ -0,0 +1,53 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// This file is run by the x509 tests to ensure that a program with minimal
+// imports can sign certificates without errors resulting from missing hash
+// functions.
+package main
+
+import (
+	"crypto/rand"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+	"math/big"
+	"time"
+)
+
+func main() {
+	block, _ := pem.Decode([]byte(pemPrivateKey))
+	rsaPriv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
+	if err != nil {
+		panic("Failed to parse private key: " + err.Error())
+	}
+
+	template := x509.Certificate{
+		SerialNumber: big.NewInt(1),
+		Subject: pkix.Name{
+			CommonName:   "test",
+			Organization: []string{"Σ Acme Co"},
+		},
+		NotBefore: time.Unix(1000, 0),
+		NotAfter:  time.Unix(100000, 0),
+		KeyUsage:  x509.KeyUsageCertSign,
+	}
+
+	if _, err = x509.CreateCertificate(rand.Reader, &template, &template, &rsaPriv.PublicKey, rsaPriv); err != nil {
+		panic("failed to create certificate with basic imports: " + err.Error())
+	}
+}
+
+var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
+MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
+fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
+/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
+RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
+EO+ZJ79TJKN5yiGBRsv5yvx1UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
+IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
+tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
+-----END RSA PRIVATE KEY-----
+`

コアとなるコードの解説

src/pkg/crypto/x509/x509.go の変更

import _ "crypto/sha256" の追加は、このコミットの最も重要な変更点です。前述の通り、これはブランクインポートであり、crypto/sha256 パッケージの init() 関数が確実に実行されるようにします。crypto/sha256 パッケージの init() 関数は、SHA-256ハッシュアルゴリズムをGoの暗号システムに登録します。これにより、crypto/x509 パッケージが証明書の署名や検証を行う際に、SHA-256ハッシュ関数が常に利用可能な状態になります。

この変更がなければ、crypto/sha256 を直接インポートしていないアプリケーションが crypto/x509 を使用した場合、SHA-256が必要な場面で「ハッシュ関数が見つからない」といったエラーが発生する可能性がありました。これは、Goのリンカが、直接参照されていないコードを最終バイナリから除外する最適化を行うためです。ブランクインポートは、この最適化を回避し、必要なコードが確実に含まれるようにするためのGoのイディオムです。

src/pkg/crypto/x509/x509_test.go および src/pkg/crypto/x509/x509_test_import.go の変更

x509_test_import.go は、crypto/x509 が最小限のインポートで証明書を生成できることを検証するための独立したテストプログラムです。このプログラムは、+build ignore ディレクティブによって通常のビルドから除外されており、x509_test.go から os/exec.Command("go", "run", "x509_test_import.go").Run() を使って外部プロセスとして実行されます。

このテストの目的は、crypto/x509 が内部的に必要なハッシュ関数(この場合はSHA-256)を適切にロードし、利用できることを保証することです。もし crypto/sha256 のブランクインポートがなければ、このテストはハッシュ関数が見つからないために失敗するでしょう。このテストの追加により、将来のGoの変更やリンカの挙動の変化によって、同様の問題が再発するのを防ぐための堅牢なチェックが導入されました。

pemPrivateKey 変数に含まれるRSA秘密鍵は、テスト用のダミーデータであり、実際のセキュリティには影響しません。これは、テスト環境で証明書署名操作をシミュレートするために使用されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード (特に crypto および crypto/x509 パッケージ)
  • Go言語のコミット履歴とGerritレビューシステム (CL 44010047)
  • Go言語のビルドシステムとリンカに関する一般的な情報
  • X.509証明書とデジタル署名に関する一般的な暗号学の知識