[インデックス 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=