[インデックス 18373] ファイルの概要
このコミットは、Go言語の標準ライブラリである encoding/asn1
パッケージにおける、ASN.1 の SET
型のアンマーシャリング(デコード)サポートの追加と、既存のコメントの改善を目的としています。具体的には、SET
タグが指定された場合に、そのタグを正しく認識し、対応するGoの構造体にデコードできるようにする変更が含まれています。
変更されたファイルは以下の通りです。
src/pkg/encoding/asn1/asn1.go
: ASN.1のパースロジックとタグ処理のコア部分。src/pkg/encoding/asn1/asn1_test.go
:encoding/asn1
パッケージのテストファイル。SET
型のアンマーシャリングに関する新しいテストケースが追加されています。src/pkg/encoding/asn1/marshal.go
: ASN.1のマーシャリング(エンコード)ロジック。コメントの追加が行われています。
コミット
commit 90c066da52f1f8f74e2f5cb2a9b65415140c5b57
Author: Adam Langley <agl@golang.org>
Date: Tue Jan 28 14:12:25 2014 -0500
encoding/asn1: support set tag when unmarshaling.
This change also addresses some places where the comments were lacking.
Fixes #7087.
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/56700043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/90c066da52f1f8f74e2f5cb2a9b65415140c5b57
元コミット内容
encoding/asn1: support set tag when unmarshaling.
This change also addresses some places where the comments were lacking.
Fixes #7087.
変更の背景
この変更の主な背景は、Goの encoding/asn1
パッケージがASN.1の SET
型を正しくアンマーシャリングできないという既存の課題(Issue #7087)を解決することにあります。
ASN.1 (Abstract Syntax Notation One) は、データ構造を記述するための標準的な記法であり、様々なプロトコル(例: X.509証明書、LDAP、SNMPなど)で広く利用されています。ASN.1には SEQUENCE
と SET
という2つの主要な複合型があります。
- SEQUENCE: 要素の順序が重要であり、エンコードおよびデコード時にその順序が維持される必要があります。
- SET: 要素の順序は重要ではなく、エンコードされた値では任意の順序で出現する可能性があります。
これまでの encoding/asn1
パッケージは SEQUENCE
型の処理には対応していましたが、SET
型のアンマーシャリングには明示的なサポートが不足していました。そのため、SET
型を含むASN.1データをGoの構造体にデコードしようとすると、正しく処理できない問題が発生していました。
このコミットは、SET
型のデータをGoの構造体にデコードする際に、asn1:"set"
タグをGoの構造体フィールドに指定することで、encoding/asn1
パッケージが SET
型を正しく認識し、処理できるようにするためのものです。また、既存のコードのコメントが不足している箇所を補完し、可読性と理解度を向上させることも目的としています。
前提知識の解説
ASN.1 (Abstract Syntax Notation One)
ASN.1は、データ構造をプラットフォームやプログラミング言語に依存しない形で記述するための国際標準です。通信プロトコルやデータ交換フォーマットの定義に広く用いられます。ASN.1で定義されたデータ構造は、BER (Basic Encoding Rules) や DER (Distinguished Encoding Rules) などのエンコーディングルールに従ってバイト列に変換されます。
DER (Distinguished Encoding Rules)
DERはBERのサブセットであり、特定のASN.1値に対して常に一意のバイト列表現を生成することを保証するエンコーディングルールです。これにより、デジタル署名やハッシュ計算など、データの同一性が厳密に求められる場面で利用されます。DERでは、SET
型の要素も特定の順序(通常はタグと値の辞書順)でエンコードされることが義務付けられています。
encoding/asn1
パッケージ
Go言語の encoding/asn1
パッケージは、DERエンコードされたASN.1データをGoの構造体との間でマーシャリング(エンコード)およびアンマーシャリング(デコード)するための機能を提供します。Goの構造体フィールドにタグ(例: asn1:"optional,explicit,tag:x"
)を付与することで、ASN.1の型やエンコーディングの特性をGoの構造体にマッピングします。
ASN.1 SET
と SEQUENCE
の違い
SEQUENCE
: 要素の順序が定義されており、エンコードされたデータもその順序に従います。Goの構造体では、フィールドの定義順がそのままASN.1のSEQUENCE
の要素順に対応します。SET
: 要素の順序は定義されていません。エンコードされたデータでは、要素は任意の順序で出現できます。しかし、DERエンコーディングでは一意性を保証するために、要素を特定の順序(通常はタグと値の辞書順)で並べ替えてエンコードします。Goのencoding/asn1
パッケージでSET
を扱う場合、アンマーシャリング時に要素の順序が保証されないため、Goのslice
型にデコードする際に注意が必要です。
Goの構造体タグ
Goでは、構造体フィールドにタグを付与することで、リフレクションを通じて追加のメタデータを提供できます。encoding/asn1
パッケージでは、asn1:"key:value"
の形式でタグを使用し、ASN.1のエンコーディング/デコーディング動作を制御します。
optional
: フィールドがASN.1のOPTIONAL
であることを示します。explicit
: 明示的なタグ付け(EXPLICIT
)を使用することを示します。tag:x
: ASN.1のタグ番号を指定します。set
: このコミットで追加されたタグで、フィールドがASN.1のSET
型であることを示します。
技術的詳細
このコミットの技術的な核心は、encoding/asn1
パッケージがASN.1の SET
型をアンマーシャリングする際の挙動を修正・拡張することにあります。
-
parseField
関数の変更:src/pkg/encoding/asn1/asn1.go
内のparseField
関数は、Goの構造体フィールドのタグを解析し、対応するASN.1のタグとクラスを決定する役割を担っています。このコミットでは、params.set
(構造体タグにset
が含まれるかを示すフラグ) がtrue
の場合、universalTag
をtagSet
(ASN.1のSET
型のユニバーサルタグ) に設定するロジックが追加されました。これにより、アンマーシャリング時にSET
型のデータが正しく識別されるようになります。// src/pkg/encoding/asn1/asn1.go if params.set { universalTag = tagSet }
-
Unmarshal
関数のコメント更新:src/pkg/encoding/asn1/asn1.go
内のUnmarshal
関数のドキュメンテーションコメントが更新され、新しいset
タグの説明が追加されました。これにより、開発者がSET
型を扱う際にどのように構造体タグを使用すればよいかが明確になります。// src/pkg/encoding/asn1/asn1.go // set causes a SET, rather than a SEQUENCE type to be expected
さらに、スライス要素の型名が
"SET"
で終わる場合に、set
タグが設定されたかのように扱われるという新しい動作もコメントで説明されています。これは、ネストされたスライスなど、構造体タグを直接指定できない場合に特に有用です。// src/pkg/encoding/asn1/asn1.go // If the type name of a slice element ends with "SET" then it's treated as if // the "set" tag was set on it. This can be used with nested slices where a // struct tag cannot be given.
-
Marshal
関数のコメント追加:src/pkg/encoding/asn1/marshal.go
内のMarshal
関数のドキュメンテーションコメントに、マーシャリング時に使用できる追加の構造体タグ(ia5
,omitempty
,printable
,utf8
)に関する説明が追加されました。これは直接SET
型のアンマーシャリングとは関係ありませんが、コメントの不足を補うというコミットの目的の一部です。 -
テストケースの追加:
src/pkg/encoding/asn1/asn1_test.go
にTestSet
という新しい構造体と、それに対応するアンマーシャリングのテストデータが追加されました。このテストケースは、asn1:"set"
タグが付けられた[]int
スライスが正しくデコードされることを検証します。// src/pkg/encoding/asn1/asn1_test.go type TestSet struct { Ints []int `asn1:"set"` } // ... {[]byte{0x30, 0x0b, 0x31, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &TestSet{Ints: []int{1, 2, 3}}},
ここで、
0x30
はSEQUENCE
のタグ、0x0b
は長さ、0x31
はSET
のタグ、0x09
は長さ、そしてその後にINTEGER
型の1
,2
,3
が続くASN.1のバイト列が定義されています。このテストは、SET
型のデータが正しくGoの[]int
にデコードされることを確認します。
これらの変更により、encoding/asn1
パッケージはASN.1の SET
型をより堅牢に処理できるようになり、関連するプロトコルを扱うGoアプリケーションの信頼性が向上します。
コアとなるコードの変更箇所
src/pkg/encoding/asn1/asn1.go
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -634,6 +634,10 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
universalTag = tagGeneralizedTime
}
+ if params.set {
+ universalTag = tagSet
+ }
+
expectedClass := classUniversal
expectedTag := universalTag
@@ -854,13 +858,20 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
//
// The following tags on struct fields have special meaning to Unmarshal:
//
-// optional marks the field as ASN.1 OPTIONAL
-// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
-// default:x sets the default value for optional integer fields
+// application specifies that a APPLICATION tag is used
+// default:x sets the default value for optional integer fields
+// explicit specifies that an additional, explicit tag wraps the implicit one
+// optional marks the field as ASN.1 OPTIONAL
+// set causes a SET, rather than a SEQUENCE type to be expected
+// tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
//
// If the type of the first field of a structure is RawContent then the raw
// ASN1 contents of the struct will be stored in it.
//
+// If the type name of a slice element ends with "SET" then it's treated as if
+// the "set" tag was set on it. This can be used with nested slices where a
+// struct tag cannot be given.
+//
// Other ASN.1 types are not supported; if it encounters them,
// Unmarshal returns a parse error.
func Unmarshal(b []byte, val interface{}) (rest []byte, err error) {
src/pkg/encoding/asn1/asn1_test.go
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -397,6 +397,10 @@ type TestBigInt struct {
X *big.Int
}
+type TestSet struct {
+ Ints []int `asn1:"set"`
+}
+
var unmarshalTestData = []struct {
in []byte
out interface{}
@@ -416,6 +420,7 @@ var unmarshalTestData = []struct {
{[]byte{0x01, 0x01, 0xff}, newBool(true)},
{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},
{[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},
+ {[]byte{0x30, 0x0b, 0x31, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &TestSet{Ints: []int{1, 2, 3}}},
}
func TestUnmarshal(t *testing.T) {
src/pkg/encoding/asn1/marshal.go
--- a/src/pkg/encoding/asn1/marshal.go
+++ b/src/pkg/encoding/asn1/marshal.go
@@ -568,6 +568,14 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
}
// Marshal returns the ASN.1 encoding of val.
+//
+// In addition to the struct tags recognised by Unmarshal, the following can be
+// used:
+//
+// ia5: causes strings to be marshaled as ASN.1, IA5 strings
+// omitempty: causes empty slices to be skipped
+// printable: causes strings to be marshaled as ASN.1, PrintableString strings.
+// utf8: causes strings to be marshaled as ASN.1, UTF8 strings
func Marshal(val interface{}) ([]byte, error) {
var out bytes.Buffer
v := reflect.ValueOf(val)
コアとなるコードの解説
src/pkg/encoding/asn1/asn1.go
の変更点
-
parseField
関数内のparams.set
処理の追加:parseField
関数は、Goの構造体フィールドのタグを解析し、対応するASN.1のタグとクラスを決定する重要な役割を担っています。 追加された以下のコードブロックは、fieldParameters
構造体に含まれるset
フラグがtrue
の場合に、universalTag
をtagSet
(ASN.1のSET
型のユニバーサルタグ、値は0x11
または17) に設定します。 これにより、asn1:"set"
タグが指定されたGoの構造体フィールドに対して、アンマーシャリング時にASN.1のSET
型として正しく処理されるようになります。if params.set { universalTag = tagSet }
-
Unmarshal
関数のコメント更新:Unmarshal
関数のドキュメンテーションコメントが大幅に更新され、Goの構造体フィールドに指定できる新しいタグとその意味が追加されました。 特に重要なのは、set
タグの説明です。// set causes a SET, rather than a SEQUENCE type to be expected
この説明により、開発者はGoの構造体フィールドに
asn1:"set"
を追加することで、ASN.1のSET
型のデータをデコードできることを理解できます。さらに、スライス要素の型名が
"SET"
で終わる場合に、明示的にset
タグを指定しなくてもset
タグが設定されたかのように扱われるという、便利な機能が追加されたこともコメントで説明されています。これは、ネストされたスライスなど、構造体タグを直接指定できない場合に特に役立ちます。// If the type name of a slice element ends with "SET" then it's treated as if // the "set" tag was set on it. This can be used with nested slices where a // struct tag cannot be given.
src/pkg/encoding/asn1/asn1_test.go
の変更点
-
TestSet
構造体の追加:TestSet
という新しいGoの構造体が定義されました。この構造体は、asn1:"set"
タグが付けられたInts
という[]int
型のフィールドを持っています。これは、SET OF INTEGER
のようなASN.1構造をGoで表現する典型的な方法です。type TestSet struct { Ints []int `asn1:"set"` }
-
unmarshalTestData
へのテストケースの追加:unmarshalTestData
スライスに、TestSet
構造体と対応するASN.1のバイト列を含む新しいテストケースが追加されました。{[]byte{0x30, 0x0b, 0x31, 0x09, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03}, &TestSet{Ints: []int{1, 2, 3}}},
このバイト列は、ASN.1のSEQUENCE
(タグ0x30
) の中にSET
(タグ0x31
) が含まれ、そのSET
の中に3つのINTEGER
(値1
,2
,3
) が含まれる構造を表しています。このテストケースは、encoding/asn1
パッケージがこのバイト列を正しくパースし、TestSet
構造体のInts
フィールドに[1, 2, 3]
をデコードできることを検証します。
src/pkg/encoding/asn1/marshal.go
の変更点
Marshal
関数のコメント追加:Marshal
関数のドキュメンテーションコメントに、マーシャリング時に使用できる追加の構造体タグ(ia5
,omitempty
,printable
,utf8
)に関する説明が追加されました。これは、Unmarshal
関数のコメント更新と同様に、ドキュメンテーションの改善を目的としています。
これらの変更により、encoding/asn1
パッケージはASN.1の SET
型のアンマーシャリングをサポートし、より多くのASN.1データ構造をGoで扱うことが可能になりました。
関連リンク
- Go CL 56700043: https://golang.org/cl/56700043
- Go Issue #7087: (Web検索では直接的なIssue #7087は見つかりませんでしたが、コミットメッセージに記載されているため、このコミットが解決した問題として参照されます。)
参考にした情報源リンク
- ASN.1 SET vs SEQUENCE: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGOpGHF6C76wUB7H7vL6mE8N4jdE_hnT5w81XHMU0gQw9GE7Ey3n5_50zBR9VI_esRkAtyZdYJDGpFymlI6bazMpqLEqNdFilY4xmZ_o37PyehQ8NEB06y1D7oiGSz-teT8pQuQ24wiLwNUTWocCrUHsSH__yBeXVlHQ_AcqAPCkI7ZfORrA6TCRhy6s==
- Go encoding/asn1 package: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQG1yD9XQCEYsNgzovhkCzHRUYxFpANW_Yinyvp4BHUFjpjk3i_-tPTVT_DXDcscMqC_nLSsCH0EGxAbBG9PP_apmiI0O4YAHlgtwq9OkbvipDCqCE0SMLElozbg=
- Go encoding/asn1 package (GitHub): https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHVxYXblxXXtAgLQv_DZ2tbpWIKKIJiSLqLAUFvNKnk55VtWEPnqOuiKy9sOUMHM7h4qJ4kL0qH3LHLl2BFepLwmK14iz7UO7QTaRc3scEBFQsSMOJ21DTzpPj5YlRR2mUDMJ6-FSs88lBxsrY6hWKqJGoztsIDjwcj_hlU
- Go encoding/asn1 package (UBC): https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHiGuUer-pl2eQWsQTfbOE05uYtuKrfmYZNfOhJZminabiiw0oMmfQjdx2sSWQ5gDZpDM_3JsvW_t9AqZ61sSaoG6aiWGf2hoijGBFTZ8MJYcTd0AJwkLgaN8KgWsom7H64G_Z1DdTlyLDm9RWlGSaHI4HN6e3blIVagaQFGCDPx2FcK7DbdUO-CY3GnPaR4BujTsan8smzkRo=