[インデックス 16405] ファイルの概要
このコミットは、Go言語の標準ライブラリ crypto/x509
パッケージにおけるエラーメッセージの統一に関するものです。具体的には、パッケージ内で発生する様々なエラーメッセージのプレフィックスを「x509: 」に統一することで、エラーハンドリングの一貫性と可読性を向上させています。
コミット
commit ddc8679128bae2c730e59f86880a3de96cf2c7d5
Author: Adam Langley <agl@golang.org>
Date: Fri May 24 16:23:13 2013 -0400
crypto/x509: harmonise error prefixes.
crypto/x509 has ended up with a variety of error formats. This change makes them all start with "x509: ".
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/9736043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ddc8679128bae2c730e59f86880a3de96cf2c7d5
元コミット内容
crypto/x509
パッケージ内のエラープレフィックスを統一する。
crypto/x509
は様々なエラーフォーマットを持つに至った。この変更は、それらすべてを「x509: 」で始まるようにする。
変更の背景
Go言語の crypto/x509
パッケージは、X.509証明書、PKCS#1、PKCS#8、SEC1などの暗号関連の標準を扱うための機能を提供します。時間の経過とともに、このパッケージ内で生成されるエラーメッセージには一貫性がなくなり、異なる形式のプレフィックスや、全くプレフィックスがないエラーメッセージが混在する状態になっていました。
このようなエラーメッセージの不統一は、以下のような問題を引き起こします。
- デバッグの困難さ: エラーメッセージの形式がバラバラだと、ログ解析やデバッグ時に特定のエラーを識別しにくくなります。
- プログラムによるエラーハンドリングの複雑化: エラー文字列を解析してエラーの種類を判別するようなコードを書く場合、形式が統一されていないと複雑なパターンマッチングが必要になります。
- ユーザー体験の低下: エラーメッセージがユーザーに表示される場合、一貫性のないメッセージは混乱を招き、プロフェッショナルでない印象を与えます。
- コードの可読性低下: エラーメッセージの生成箇所が多岐にわたる場合、それぞれが異なるスタイルで記述されていると、コード全体の可読性が損なわれます。
このコミットは、これらの問題を解決し、crypto/x509
パッケージのエラーメッセージに統一された「x509: 」プレフィックスを付与することで、エラーハンドリングの品質と開発者体験を向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の概念に関する基本的な知識が役立ちます。
-
Go言語のエラーハンドリング:
- Go言語では、エラーは
error
インターフェースを実装する値として扱われます。 - 関数は通常、最後の戻り値として
error
型の値を返します。エラーがない場合はnil
を返します。 - エラーメッセージは
errors.New("message")
やfmt.Errorf("format %v", value)
を使用して作成されます。 - エラーメッセージの文字列は、デバッグやログ出力、あるいはプログラムによるエラーの識別(Go 1.13以降の
errors.Is
やerrors.As
のような機能が導入される前は、文字列比較が一般的でした)に利用されます。
- Go言語では、エラーは
-
crypto/x509
パッケージ:- Go言語の標準ライブラリの一部であり、X.509証明書、公開鍵基盤(PKI)の操作、PKCS#1(RSA公開鍵暗号標準)、PKCS#8(秘密鍵情報構文標準)、SEC1(楕円曲線暗号の秘密鍵標準)などの暗号関連の標準を実装しています。
- このパッケージは、TLS/SSL通信、コード署名、データ暗号化など、セキュリティが重要なアプリケーションで広く利用されます。
- 証明書のパース、検証、鍵の生成とパースなど、多岐にわたる機能を提供します。
-
エラープレフィックスの重要性:
- 大規模なシステムでは、様々なコンポーネントからエラーが報告されます。エラーメッセージに特定のプレフィックス(例:
x509:
,net:
,io:
) を付けることで、そのエラーがどのサブシステムやパッケージから発生したのかを一目で識別できるようになります。 - これにより、エラーの発生源を特定しやすくなり、デバッグや問題解決の効率が向上します。また、ログ収集システムや監視ツールでエラーをフィルタリングする際にも役立ちます。
- 大規模なシステムでは、様々なコンポーネントからエラーが報告されます。エラーメッセージに特定のプレフィックス(例:
技術的詳細
このコミットの技術的な変更は非常にシンプルですが、その影響は広範にわたります。主な変更点は、crypto/x509
パッケージ内のエラーメッセージ文字列の先頭に "x509: "
というプレフィックスを追加することです。
具体的には、以下のファイルが変更されています。
src/pkg/crypto/x509/pkcs1.go
: PKCS#1形式のRSA秘密鍵のパースに関連するエラーメッセージ。src/pkg/crypto/x509/pkcs8.go
: PKCS#8形式の秘密鍵のパースに関連するエラーメッセージ。src/pkg/crypto/x509/sec1.go
: SEC1形式の楕円曲線秘密鍵のパースに関連するエラーメッセージ。src/pkg/crypto/x509/x509.go
: X.509証明書全般のパース、検証、公開鍵の処理など、より広範なエラーメッセージ。
変更前は、エラーメッセージが errors.New("private key contains zero or negative value")
のように直接定義されていたり、fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
のように crypto/x509:
というプレフィックスが既に付いているもの、あるいは全く異なる形式で定義されているものがありました。
このコミットでは、これらのエラーメッセージを errors.New("x509: private key contains zero or negative value")
や fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
のように統一しています。
この変更は、Go言語のエラーハンドリングのベストプラクティスに沿ったものです。Goの標準ライブラリでは、通常、エラーを返すパッケージ名や機能名をプレフィックスとしてエラーメッセージに含めることが推奨されています。これにより、エラーが発生した際に、どのコンポーネントがエラーを報告しているのかが明確になり、デバッグやエラーの分類が容易になります。
例えば、net
パッケージは net: unknown host
のようなエラーを返し、io
パッケージは io: read/write on closed pipe
のようなエラーを返します。このコミットは、crypto/x509
パッケージも同様の慣習に従うように修正したものです。
コアとなるコードの変更箇所
このコミットでは、主に errors.New
および fmt.Errorf
で生成されるエラー文字列に "x509: "
というプレフィックスを追加しています。
以下に、各ファイルでの変更例を抜粋します。
src/pkg/crypto/x509/pkcs1.go
--- a/src/pkg/crypto/x509/pkcs1.go
+++ b/src/pkg/crypto/x509/pkcs1.go
@@ -52,7 +52,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) {
}\n \n if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
-\t\treturn nil, errors.New("private key contains zero or negative value")
+\t\treturn nil, errors.New("x509: private key contains zero or negative value")
}\n \n key = new(rsa.PrivateKey)
@@ -67,7 +67,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) {
key.Primes[1] = priv.Q
for i, a := range priv.AdditionalPrimes {
if a.Prime.Sign() <= 0 {
-\t\t\treturn nil, errors.New("private key contains zero or negative prime")
+\t\t\treturn nil, errors.New("x509: private key contains zero or negative prime")
}
key.Primes[i+2] = a.Prime
// We ignore the other two values because rsa will calculate
src/pkg/crypto/x509/pkcs8.go
--- a/src/pkg/crypto/x509/pkcs8.go
+++ b/src/pkg/crypto/x509/pkcs8.go
@@ -32,7 +32,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
if err != nil {
-\t\t\treturn nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
+\t\t\treturn nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
}
return key, nil
@@ -44,11 +44,11 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
}
key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
if err != nil {
-\t\t\treturn nil, errors.New("crypto/x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
+\t\t\treturn nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
}
return key, nil
default:
-\t\treturn nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
+\t\treturn nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
}
}
src/pkg/crypto/x509/sec1.go
--- a/src/pkg/crypto/x509/sec1.go
+++ b/src/pkg/crypto/x509/sec1.go
@@ -40,10 +40,10 @@ func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
var privKey ecPrivateKey
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
-\t\treturn nil, errors.New("crypto/x509: failed to parse EC private key: " + err.Error())
+\t\treturn nil, errors.New("x509: failed to parse EC private key: " + err.Error())
}\n if privKey.Version != ecPrivKeyVersion {
-\t\treturn nil, fmt.Errorf("crypto/x509: unknown EC private key version %d", privKey.Version)
+\t\treturn nil, fmt.Errorf("x509: unknown EC private key version %d", privKey.Version)
}\n \n var curve elliptic.Curve
@@ -53,12 +53,12 @@ func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *e
curve = namedCurveFromOID(privKey.NamedCurveOID)
}
if curve == nil {
-\t\treturn nil, errors.New("crypto/x509: unknown elliptic curve")
+\t\treturn nil, errors.New("x509: unknown elliptic curve")
}\n \n k := new(big.Int).SetBytes(privKey.PrivateKey)
if k.Cmp(curve.Params().N) >= 0 {
-\t\treturn nil, errors.New("crypto/x509: invalid elliptic curve private key value")
+\t\treturn nil, errors.New("x509: invalid elliptic curve private key value")
}\n priv := new(ecdsa.PrivateKey)
priv.Curve = curve
src/pkg/crypto/x509/x509.go
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -40,7 +40,7 @@ func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) {
}\n algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm)
if algo == UnknownPublicKeyAlgorithm {
-\t\treturn nil, errors.New("ParsePKIXPublicKey: unknown public key algorithm")
+\t\treturn nil, errors.New("x509: unknown public key algorithm")
}\n return parsePublicKey(algo, &pki)
}\n@@ -56,7 +56,7 @@ func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) {
E: pub.E,
})
default:
-\t\treturn nil, errors.New("MarshalPKIXPublicKey: unknown public key type")
+\t\treturn nil, errors.New("x509: unknown public key type")
}\n \n pkix := pkixPublicKey{
@@ -477,7 +477,7 @@ type Certificate struct {
// ErrUnsupportedAlgorithm results from attempting to perform an operation that
// involves algorithms that are not currently implemented.
-var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature: algorithm unimplemented")
+var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented")
// ConstraintViolationError results when a requested usage is not permitted by
// a certificate. For example: checking a signature when the public key isn\'t a
@@ -485,7 +485,7 @@ var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature:
type ConstraintViolationError struct{}\n \n func (ConstraintViolationError) Error() string {
-\treturn "crypto/x509: invalid signature: parent certificate cannot sign this kind of certificate"
+\treturn "x509: invalid signature: parent certificate cannot sign this kind of certificate"
}\n \n func (c *Certificate) Equal(other *Certificate) bool {
@@ -604,10 +604,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return err
}
if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
-\t\t\treturn errors.New("DSA signature contained zero or negative values")
+\t\t\treturn errors.New("x509: DSA signature contained zero or negative values")
}
if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) {
-\t\t\treturn errors.New("DSA verification failure")
+\t\t\treturn errors.New("x509: DSA verification failure")
}
return
case *ecdsa.PublicKey:
@@ -616,10 +616,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
-\t\t\treturn errors.New("crypto/x509: ECDSA signature contained zero or negative values")
+\t\t\treturn errors.New("x509: ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
-\t\t\treturn errors.New("crypto/x509: ECDSA verification failure")
+\t\t\treturn errors.New("x509: ECDSA verification failure")
}
return
}\n@@ -635,7 +635,7 @@ func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err error) {
type UnhandledCriticalExtension struct{}\n \n func (h UnhandledCriticalExtension) Error() string {
-\treturn "unhandled critical extension"
+\treturn "x509: unhandled critical extension"
}\n \n type basicConstraints struct {
@@ -694,7 +694,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{\
return nil, err
}
if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 {
-\t\t\treturn nil, errors.New("zero or negative DSA parameter")
+\t\t\treturn nil, errors.New("x509: zero or negative DSA parameter")
}
pub := &dsa.PublicKey{
Parameters: dsa.Parameters{
@@ -714,11 +714,11 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{\
}
namedCurve := namedCurveFromOID(*namedCurveOID)
if namedCurve == nil {
-\t\t\treturn nil, errors.New("crypto/x509: unsupported elliptic curve")
+\t\t\treturn nil, errors.New("x509: unsupported elliptic curve")
}
x, y := elliptic.Unmarshal(namedCurve, asn1Data)
if x == nil {
-\t\t\treturn nil, errors.New("crypto/x509: failed to unmarshal elliptic curve point")
+\t\t\treturn nil, errors.New("x509: failed to unmarshal elliptic curve point")
}
pub := &ecdsa.PublicKey{
Curve: namedCurve,
@@ -752,7 +752,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
}\n \n if in.TBSCertificate.SerialNumber.Sign() < 0 {
-\t\treturn nil, errors.New("negative serial number")
+\t\treturn nil, errors.New("x509: negative serial number")
}\n \n out.Version = in.TBSCertificate.Version + 1
コアとなるコードの解説
上記の変更箇所は、Go言語の errors
パッケージと fmt
パッケージを使用してエラーメッセージを生成している部分です。
-
errors.New("...")
: これは、静的なエラーメッセージを生成するための最も基本的な方法です。このコミットでは、既存の静的エラー文字列の先頭に"x509: "
を追加しています。- 例:
errors.New("private key contains zero or negative value")
がerrors.New("x509: private key contains zero or negative value")
に変更。
- 例:
-
fmt.Errorf("...", args...)
: これは、フォーマット文字列と可変引数を使用して動的なエラーメッセージを生成する方法です。このコミットでは、フォーマット文字列の先頭に"x509: "
を追加しています。- 例:
fmt.Errorf("crypto/x509: unknown EC private key version %d", privKey.Version)
がfmt.Errorf("x509: unknown EC private key version %d", privKey.Version)
に変更。 - 注目すべきは、元々
"crypto/x509: "
というプレフィックスが付いていた箇所も、より簡潔な"x509: "
に変更されている点です。これは、パッケージ名が既にx509
であるため、冗長なcrypto/
を省略し、より短く一貫性のある形式に統一する意図があると考えられます。
- 例:
これらの変更は、コードのロジック自体には影響を与えません。影響を受けるのは、エラーが発生した際にアプリケーションやユーザーに報告されるエラーメッセージの文字列形式のみです。しかし、この変更により、crypto/x509
パッケージから発生するエラーの識別と処理が大幅に改善されます。
関連リンク
- Go言語の
errors
パッケージ: https://pkg.go.dev/errors - Go言語の
fmt
パッケージ: https://pkg.go.dev/fmt - Go言語の
crypto/x509
パッケージ: https://pkg.go.dev/crypto/x509 - Go Code Review Comments - Error messages: https://go.dev/wiki/CodeReviewComments#error-messages (Goのエラーメッセージに関する慣習について説明されています)
参考にした情報源リンク
- 上記のGitHubコミットページ
- Go言語の公式ドキュメント
- Go言語のエラーハンドリングに関する一般的な情報源 (例: Go by Example: Errors, Effective Go: Errors)