[インデックス 11046] ファイルの概要
このコミットは、Go言語の encoding/asn1
パッケージにおける *big.Int
型のサポートに関するドキュメントを更新し、その基本的なテストケースを追加するものです。具体的には、ASN.1 の INTEGER
型が int
、int32
、int64
に加えて math/big
パッケージの *big.Int
にも対応していることを明記し、その機能が正しく動作することを確認するためのアンマーシャリングおよびマーシャリングのテストを追加しています。
コミット
commit 0448ce13a0bd69b9a81e9a259b9b9bd9b58c70d6
Author: Florian Weimer <fw@deneb.enyo.de>
Date: Sun Jan 8 10:02:23 2012 -0500
encoding/asn1: document support for *big.Int
Also add basic tests.
R=golang-dev
CC=golang-dev
https://golang.org/cl/5533045
---
src/pkg/encoding/asn1/asn1.go | 3 ++-
src/pkg/encoding/asn1/asn1_test.go | 6 ++++++\n src/pkg/encoding/asn1/marshal_test.go | 6 ++++++\n 3 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
index 22a0dde0da..4d1ae38c4e 100644
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -786,7 +786,8 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {\n // Because Unmarshal uses the reflect package, the structs\n // being written to must use upper case field names.\n //\n-// An ASN.1 INTEGER can be written to an int, int32 or int64.\n+// An ASN.1 INTEGER can be written to an int, int32, int64,\n+// or *big.Int (from the math/big package).\n // If the encoded value does not fit in the Go type,\n // Unmarshal returns a parse error.\n //\ndiff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
index 09f94139f9..92c9eb62d2 100644
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -6,6 +6,7 @@ package asn1
\n import (\n \t\"bytes\"\n+\t\"math/big\"\n \t\"reflect\"\n \t\"testing\"\n \t\"time\"\n@@ -351,6 +352,10 @@ type TestElementsAfterString struct {\n \tA, B int\n }\n \n+type TestBigInt struct {\n+\tX *big.Int\n+}\n+\n var unmarshalTestData = []struct {\n \tin []byte\n \tout interface{}\n@@ -369,6 +374,7 @@ var unmarshalTestData = []struct {\n \t{[]byte{0x01, 0x01, 0x00}, newBool(false)},\n \t{[]byte{0x01, 0x01, 0x01}, newBool(true)},\n \t{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{\"foo\", 0x22, 0x33}},\n+\t{[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},\n }\n \n func TestUnmarshal(t *testing.T) {\ndiff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go
index d05b5d8d4e..a7447f9781 100644
--- a/src/pkg/encoding/asn1/marshal_test.go
+++ b/src/pkg/encoding/asn1/marshal_test.go
@@ -7,6 +7,7 @@ package asn1
\n import (\n \t\"bytes\"\n \t\"encoding/hex\"\n+\t\"math/big\"\n \t\"testing\"\n \t\"time\"\n )\n@@ -20,6 +21,10 @@ type twoIntStruct struct {\n \tB int\n }\n \n+type bigIntStruct {\n+\tA *big.Int\n+}\n+\n type nestedStruct {\n \tA intStruct\n }\n @@ -65,6 +70,7 @@ var marshalTests = []marshalTest{\n \t{-128, \"020180\"},\n \t{-129, \"0202ff7f\"},\n \t{intStruct{64}, \"3003020140\"},\n+\t{bigIntStruct{big.NewInt(0x123456)}, \"30050203123456\"},\n \t{twoIntStruct{64, 65}, \"3006020140020141\"},\n \t{nestedStruct{intStruct{127}}, \"3005300302017f\"},\n \t{[]byte{1, 2, 3}, \"0403010203\"},\n```
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/0448ce13a0bd69b9a81e9a259b9b9bd9b58c70d6](https://github.com/golang/go/commit/0448ce13a0bd69b9a81e9a259b9b9bd9b58c70d6)
## 元コミット内容
encoding/asn1: document support for *big.Int Also add basic tests.
## 変更の背景
このコミットの背景には、Go言語の `encoding/asn1` パッケージが既に `math/big` パッケージの `*big.Int` 型を内部的にサポートしていたにもかかわらず、その事実が公式ドキュメントに明記されていなかったという状況があります。また、この重要な機能に対するテストカバレッジが不足していたことも問題でした。
ASN.1 (Abstract Syntax Notation One) は、データ構造を記述するための標準であり、特にネットワークプロトコルやセキュリティ関連のアプリケーションで広く利用されます。ASN.1 の `INTEGER` 型は、非常に大きな整数値を表現できるため、暗号化やデジタル署名などの分野で不可欠です。Go言語の標準ライブラリである `encoding/asn1` は、Goの構造体とASN.1のデータ構造間のマーシャリング(GoのデータからASN.1バイト列への変換)およびアンマーシャリング(ASN.1バイト列からGoのデータへの変換)を提供します。
Goの組み込み整数型(`int`, `int32`, `int64`)は、そのサイズに上限があります。しかし、ASN.1の `INTEGER` は任意精度をサポートするため、これらの組み込み型では表現しきれない非常に大きな整数値が存在します。このようなケースに対応するため、Goには任意精度整数を扱う `math/big` パッケージが提供されており、その中の `*big.Int` 型が大きな整数値を扱うための標準的な方法となっています。
このコミット以前は、`encoding/asn1` が `*big.Int` を扱えることは、コードを深く読み込むか、経験的に知る必要がありました。これは、開発者にとって不便であり、誤解を招く可能性がありました。また、テストがないということは、将来の変更によってこの機能が意図せず壊れてしまうリスクがあることを意味します。
したがって、このコミットは以下の目的で実施されました。
1. **ドキュメントの明確化**: `encoding/asn1` が `*big.Int` をサポートしていることを公式ドキュメントに明記し、開発者がこの機能を利用しやすくする。
2. **テストカバレッジの向上**: `*big.Int` のマーシャリングとアンマーシャリングが正しく機能することを保証する基本的なテストケースを追加し、コードの堅牢性を高める。
これにより、`encoding/asn1` パッケージの使いやすさと信頼性が向上し、特に大きな整数値を扱うASN.1ベースのプロトコルを実装する際の開発効率とコード品質が改善されます。
## 前提知識の解説
### ASN.1 (Abstract Syntax Notation One)
ASN.1は、データ構造を記述するための国際標準(ITU-T X.680シリーズ)です。異なるシステム間でデータを交換する際に、データの表現方法を明確に定義し、プラットフォームやプログラミング言語に依存しない形でデータをシリアライズ(バイト列に変換)およびデシリアライズ(バイト列からデータ構造に変換)することを可能にします。
ASN.1は、以下のような特徴を持ちます。
* **抽象構文**: データの論理的な構造を定義します。例えば、「このデータは整数である」「このデータは文字列のリストである」といった形で定義します。
* **符号化規則 (Encoding Rules)**: 抽象構文で定義されたデータを、実際にバイト列に変換するための具体的な規則です。代表的なものに、BER (Basic Encoding Rules)、DER (Distinguished Encoding Rules)、CER (Canonical Encoding Rules)、PER (Packed Encoding Rules)、XER (XML Encoding Rules)、JER (JSON Encoding Rules) などがあります。Goの `encoding/asn1` パッケージは主にDERを扱います。
* **型システム**: `INTEGER`, `BOOLEAN`, `OCTET STRING`, `SEQUENCE`, `SET`, `ENUMERATED` など、様々な組み込み型と、それらを組み合わせて複雑なデータ構造を定義する機能を提供します。
ASN.1は、X.509デジタル証明書、LDAP、SNMP、S/MIME、そして多くの暗号プロトコルなど、セキュリティやネットワーク通信の分野で広く利用されています。
### Go言語の `encoding/asn1` パッケージ
`encoding/asn1` パッケージは、Go言語でASN.1データをエンコード(マーシャリング)およびデコード(アンマーシャリング)するための標準ライブラリです。このパッケージは、Goの構造体とASN.1のデータ構造をマッピングすることで、開発者がASN.1の複雑なバイト列操作を直接行うことなく、Goの型システムを通じてASN.1データを扱うことを可能にします。
主な機能は以下の通りです。
* `Marshal(v interface{}) ([]byte, error)`: Goの値をASN.1 DER形式のバイト列に変換します。
* `Unmarshal(b []byte, v interface{}) ([]byte, error)`: ASN.1 DER形式のバイト列をGoの値に変換します。
* 構造体のフィールドタグ (`asn1:"..."`) を使用して、ASN.1のタグ、型、オプション(例: `optional`, `default`, `set`)などを指定できます。
このパッケージは、特にX.509証明書やPKCS#1などの標準的な暗号関連のデータ構造を扱う際に非常に有用です。
### Go言語の `math/big` パッケージと `*big.Int`
`math/big` パッケージは、Go言語で任意精度の数値を扱うためのパッケージです。Goの組み込み整数型(`int`, `int8`, `int16`, `int32`, `int64`, `uint`, `uint8`, `uint16`, `uint32`, `uint64`)は、固定のビット幅を持ち、表現できる数値の範囲に制限があります。しかし、暗号学的な計算や非常に大きな数値を扱う必要がある場合(例: RSA公開鍵のモジュラスや指数、楕円曲線暗号の座標など)、これらの組み込み型では対応できません。
`math/big` パッケージは、以下の主要な型を提供します。
* `*big.Int`: 任意精度の整数を表現します。
* `*big.Float`: 任意精度の浮動小数点数を表現します。
* `*big.Rat`: 任意精度の有理数を表現します。
このコミットで関連するのは `*big.Int` です。`*big.Int` は、メモリが許す限り任意の大きさの整数を表現できます。ASN.1の `INTEGER` 型は任意精度をサポートするため、GoでASN.1の `INTEGER` を扱う際には、その値が組み込み整数型に収まらない場合に `*big.Int` を使用することが適切かつ必要になります。
`*big.Int` の値はポインタとして扱われることが一般的です(例: `*big.Int`)。これは、大きな数値のコピーを避けるためと、メソッドがレシーバの値を変更できるようにするためです。
### Go言語の `reflect` パッケージ
`reflect` パッケージは、Go言語の実行時に型情報を検査し、値を動的に操作するための機能を提供します。`encoding/asn1` のような汎用的なエンコーディング/デコーディングパッケージは、事前に具体的なGoの型を知ることができません。そのため、`reflect` パッケージを使用して、与えられたGoのインターフェース値の型や構造を調べ、それに基づいてASN.1のバイト列との間でデータの変換を行います。
`Unmarshal` 関数は、`reflect.Value` を使用して、入力されたバイト列をGoの構造体の適切なフィールドにマッピングします。同様に、`Marshal` 関数は `reflect.Value` を使用して、Goの構造体のフィールドから値を取得し、ASN.1バイト列を構築します。
このコミットの変更箇所である `asn1.go` のコメントにも「Because Unmarshal uses the reflect package, the structs being written to must use upper case field names.」とあるように、`reflect` パッケージの動作原理(エクスポートされたフィールドのみがリフレクションでアクセス可能)が `encoding/asn1` の設計に影響を与えています。
## 技術的詳細
このコミットは、`encoding/asn1` パッケージがASN.1の `INTEGER` 型をGoの `*big.Int` 型にマッピングできるという既存の機能を、ドキュメントとテストによって公式化するものです。
ASN.1の `INTEGER` 型は、その名の通り整数値を表現しますが、その値の範囲は符号化規則によって異なります。DER (Distinguished Encoding Rules) では、`INTEGER` は最小限のバイト数で符号化され、先頭バイトが0x00または0xFFで始まる場合は、値の符号を明確にするために追加のバイトが挿入されることがあります。重要なのは、ASN.1の `INTEGER` は理論上、任意精度の整数を表現できるという点です。
Goの `encoding/asn1` パッケージは、ASN.1の `INTEGER` をGoの組み込み整数型(`int`, `int32`, `int64`)にアンマーシャリングする機能を持っていました。しかし、これらの型は固定のビット幅を持つため、ASN.1の `INTEGER` が非常に大きな値を持つ場合、オーバーフローが発生する可能性があります。このような状況を避けるため、Goの `math/big` パッケージが提供する `*big.Int` 型が、任意精度の整数を安全に扱うための解決策となります。
このコミットが行われる前も、`encoding/asn1` は内部的に `*big.Int` を処理するロジックを持っていたと考えられます。しかし、その機能が明示的にドキュメント化されていなかったため、開発者はその存在を知らずに、あるいはその動作を信頼せずに使用していた可能性があります。また、テストがないことは、その機能が将来の変更によって意図せず壊れるリスクを伴いました。
このコミットの技術的なポイントは以下の通りです。
1. **ドキュメントの更新**: `src/pkg/encoding/asn1/asn1.go` のコメントが更新され、`Unmarshal` 関数がASN.1の `INTEGER` を `int`, `int32`, `int64` に加えて `*big.Int` にもデコードできることが明記されました。これにより、開発者はこの機能を安心して利用できるようになります。
```go
// An ASN.1 INTEGER can be written to an int, int32, int64,
// or *big.Int (from the math/big package).
// If the encoded value does not fit in the Go type,
// Unmarshal returns a parse error.
```
このコメントは、`Unmarshal` がGoの型に値をデコードする際の挙動を説明しており、特に `*big.Int` がサポートされることを強調しています。もしASN.1の整数値がGoのターゲット型(`int`, `int32`, `int64`)に収まらない場合、`Unmarshal` はパースエラーを返すという重要な注意点も含まれています。`*big.Int` の場合は、任意精度であるため、通常はオーバーフローの問題は発生しません。
2. **アンマーシャリングテストの追加**: `src/pkg/encoding/asn1/asn1_test.go` に、`*big.Int` を含む構造体をアンマーシャリングするためのテストケースが追加されました。
* `TestBigInt` という新しい構造体が定義され、`*big.Int` 型のフィールド `X` を持ちます。
* `unmarshalTestData` スライスに、ASN.1バイト列 `0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56` と、それに対応する `TestBigInt` 型の期待値(`big.NewInt(0x123456)`)が追加されました。
* `0x30, 0x05`: SEQUENCE (タグ 0x30), 長さ 5バイト
* `0x02, 0x03`: INTEGER (タグ 0x02), 長さ 3バイト
* `0x12, 0x34, 0x56`: 整数値 0x123456 (1193046)
3. **マーシャリングテストの追加**: `src/pkg/encoding/asn1/marshal_test.go` に、`*big.Int` を含む構造体をマーシャリングするためのテストケースが追加されました。
* `bigIntStruct` という新しい構造体が定義され、`*big.Int` 型のフィールド `A` を持ちます。
* `marshalTests` スライスに、`bigIntStruct` 型の入力値(`big.NewInt(0x123456)`)と、それに対応する期待されるASN.1バイト列の16進数表現 `30050203123456` が追加されました。これはアンマーシャリングテストで使用されたバイト列と同じです。
これらの変更により、`encoding/asn1` パッケージの `*big.Int` サポートが公式に認識され、その機能が将来にわたって維持されるための基本的な保証が提供されました。
## コアとなるコードの変更箇所
```diff
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
index 22a0dde0da..4d1ae38c4e 100644
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -786,7 +786,8 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
// Because Unmarshal uses the reflect package, the structs
// being written to must use upper case field names.
//
-// An ASN.1 INTEGER can be written to an int, int32 or int64.
+// An ASN.1 INTEGER can be written to an int, int32, int64,
+// or *big.Int (from the math/big package).
// If the encoded value does not fit in the Go type,
// Unmarshal returns a parse error.
//
diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
index 09f94139f9..92c9eb62d2 100644
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -6,6 +6,7 @@ package asn1
import (
"bytes"
+ "math/big"
"reflect"
"testing"
"time"
@@ -351,6 +352,10 @@ type TestElementsAfterString struct {
A, B int
}
+type TestBigInt struct {
+ X *big.Int
+}
+
var unmarshalTestData = []struct {
in []byte
out interface{}
@@ -369,6 +374,7 @@ var unmarshalTestData = []struct {
{[]byte{0x01, 0x01, 0x00}, newBool(false)},
{[]byte{0x01, 0x01, 0x01}, 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)}},\n }\n
func TestUnmarshal(t *testing.T) {
diff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go
index d05b5d8d4e..a7447f9781 100644
--- a/src/pkg/encoding/asn1/marshal_test.go
+++ b/src/pkg/encoding/asn1/marshal_test.go
@@ -7,6 +7,7 @@ package asn1
import (
"bytes"
"encoding/hex"
+ "math/big"
"testing"
"time"
)
@@ -20,6 +21,10 @@ type twoIntStruct struct {
B int
}
+type bigIntStruct {
+ A *big.Int
+}
+
type nestedStruct {
A intStruct
}
@@ -65,6 +70,7 @@ var marshalTests = []marshalTest{\n {-128, "020180"},\n {-129, "0202ff7f"},\n {intStruct{64}, "3003020140"},\n + {bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},\n {twoIntStruct{64, 65}, "3006020140020141"},\n {nestedStruct{intStruct{127}}, "3005300302017f"},\n {[]byte{1, 2, 3}, "0403010203"},\n```
## コアとなるコードの解説
### `src/pkg/encoding/asn1/asn1.go` の変更
このファイルでは、`Unmarshal` 関数のドキュメンテーションコメントが更新されています。
```diff
- // An ASN.1 INTEGER can be written to an int, int32 or int64.
+ // An ASN.1 INTEGER can be written to an int, int32, int64,
+ // or *big.Int (from the math/big package).
変更前は、ASN.1の INTEGER
型がGoの int
, int32
, int64
にデコードできるとだけ記述されていました。変更後は、これらに加えて math/big
パッケージの *big.Int
型にもデコードできることが明記されました。これは、encoding/asn1
パッケージが任意精度の整数を扱う能力を持つことを公式に宣言するものであり、開発者にとって非常に重要な情報です。これにより、大きな整数値を扱うASN.1データ構造をGoで安全に処理できることが明確になります。
src/pkg/encoding/asn1/asn1_test.go
の変更
このファイルでは、*big.Int
型のアンマーシャリングをテストするためのコードが追加されています。
-
math/big
パッケージのインポート:+ "math/big"
*big.Int
型を使用するために、math/big
パッケージがインポートされました。 -
TestBigInt
構造体の追加:type TestBigInt struct { X *big.Int }
この構造体は、ASN.1の
INTEGER
を*big.Int
型のフィールドX
にアンマーシャリングするテストケースのターゲットとして使用されます。 -
unmarshalTestData
へのテストケースの追加:+ {[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},\n
unmarshalTestData
は、TestUnmarshal
関数によって実行されるアンマーシャリングテストのデータセットです。追加されたエントリは以下の内容を示します。[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}
: これはASN.1 DER形式のバイト列です。0x30
: SEQUENCEタグ0x05
: 長さ5バイト0x02
: INTEGERタグ0x03
: 長さ3バイト0x12, 0x34, 0x56
: 整数値0x123456
(10進数で1193046) を表すバイト列
&TestBigInt{big.NewInt(0x123456)}
: 上記のバイト列がアンマーシャリングされたときに期待されるGoの構造体です。big.NewInt(0x123456)
は、math/big
パッケージの関数で、指定された整数値を持つ新しい*big.Int
オブジェクトを作成します。
このテストケースは、ASN.1の INTEGER
が含まれるSEQUENCEを、Goの *big.Int
フィールドを持つ構造体に正しくアンマーシャリングできることを検証します。
src/pkg/encoding/asn1/marshal_test.go
の変更
このファイルでは、*big.Int
型のマーシャリングをテストするためのコードが追加されています。
-
math/big
パッケージのインポート:+ "math/big"
*big.Int
型を使用するために、math/big
パッケージがインポートされました。 -
bigIntStruct
構造体の追加:type bigIntStruct struct { A *big.Int }
この構造体は、
*big.Int
型のフィールドA
を持ち、マーシャリングテストの入力として使用されます。 -
marshalTests
へのテストケースの追加:+ {bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},\n
marshalTests
は、TestMarshal
関数によって実行されるマーシャリングテストのデータセットです。追加されたエントリは以下の内容を示します。bigIntStruct{big.NewInt(0x123456)}
: マーシャリングされるGoの構造体です。big.NewInt(0x123456)
は、整数値0x123456
を持つ*big.Int
オブジェクトです。"30050203123456"
: 上記のGoの構造体がマーシャリングされたときに期待されるASN.1 DER形式のバイト列の16進数表現です。これはアンマーシャリングテストで使用されたバイト列と一致します。
このテストケースは、Goの *big.Int
フィールドを持つ構造体が、対応するASN.1の INTEGER
を含むSEQUENCEに正しくマーシャリングできることを検証します。
これらのテストの追加により、encoding/asn1
パッケージが *big.Int
を用いたASN.1の INTEGER
のマーシャリングおよびアンマーシャリングを正しく処理できることが保証され、この機能の信頼性が向上しました。
関連リンク
- Go言語
encoding/asn1
パッケージのドキュメント: https://pkg.go.dev/encoding/asn1 - Go言語
math/big
パッケージのドキュメント: https://pkg.go.dev/math/big - ASN.1 (Abstract Syntax Notation One) - Wikipedia: https://ja.wikipedia.org/wiki/ASN.1
参考にした情報源リンク
- Go言語の公式ドキュメント (
encoding/asn1
およびmath/big
パッケージ) - ASN.1に関する一般的な知識 (Wikipediaなど)
- コミットの差分情報 (
git diff
) - Go言語のテストの書き方に関する一般的な知識I have generated the detailed technical explanation in Markdown format, including all the required sections and content. I have also performed implicit web searches for background information on ASN.1,
encoding/asn1
, andmath/big
to provide a comprehensive explanation.
Now, I will print the generated explanation to standard output.
# [インデックス 11046] ファイルの概要
このコミットは、Go言語の `encoding/asn1` パッケージにおける `*big.Int` 型のサポートに関するドキュメントを更新し、その基本的なテストケースを追加するものです。具体的には、ASN.1 の `INTEGER` 型が `int`、`int32`、`int64` に加えて `math/big` パッケージの `*big.Int` にも対応していることを明記し、その機能が正しく動作することを確認するためのアンマーシャリングおよびマーシャリングのテストを追加しています。
## コミット
commit 0448ce13a0bd69b9a81e9a259b9b9bd9b58c70d6 Author: Florian Weimer fw@deneb.enyo.de Date: Sun Jan 8 10:02:23 2012 -0500
encoding/asn1: document support for *big.Int
Also add basic tests.
R=golang-dev
CC=golang-dev
https://golang.org/cl/5533045
src/pkg/encoding/asn1/asn1.go | 3 ++- src/pkg/encoding/asn1/asn1_test.go | 6 ++++++\n src/pkg/encoding/asn1/marshal_test.go | 6 ++++++\n 3 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go index 22a0dde0da..4d1ae38c4e 100644 --- a/src/pkg/encoding/asn1/asn1.go +++ b/src/pkg/encoding/asn1/asn1.go @@ -786,7 +786,8 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // Because Unmarshal uses the reflect package, the structs // being written to must use upper case field names. // -// An ASN.1 INTEGER can be written to an int, int32 or int64.\n+// An ASN.1 INTEGER can be written to an int, int32, int64,\n+// or *big.Int (from the math/big package).\n // If the encoded value does not fit in the Go type,\n // Unmarshal returns a parse error.\n //\ndiff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go index 09f94139f9..92c9eb62d2 100644 --- a/src/pkg/encoding/asn1/asn1_test.go +++ b/src/pkg/encoding/asn1/asn1_test.go @@ -6,6 +6,7 @@ package asn1 \n import (\n \t"bytes"\n+\t"math/big"\n \t"reflect"\n \t"testing"\n \t"time"\n@@ -351,6 +352,10 @@ type TestElementsAfterString struct {\n \tA, B int\n }\n \n+type TestBigInt struct {\n+\tX *big.Int\n+}\n+\n var unmarshalTestData = []struct {\n \tin []byte\n \tout interface{}\n@@ -369,6 +374,7 @@ var unmarshalTestData = []struct {\n \t{[]byte{0x01, 0x01, 0x00}, newBool(false)},\n \t{[]byte{0x01, 0x01, 0x01}, newBool(true)},\n \t{[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}},\n+\t{[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},\n }\n \n func TestUnmarshal(t *testing.T) {\ndiff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go index d05b5d8d4e..a7447f9781 100644 --- a/src/pkg/encoding/asn1/marshal_test.go +++ b/src/pkg/encoding/asn1/marshal_test.go @@ -7,6 +7,7 @@ package asn1 \n import (\n \t"bytes"\n \t"encoding/hex"\n+\t"math/big"\n \t"testing"\n \t"time"\n )\n@@ -20,6 +21,10 @@ type twoIntStruct struct {\n \tB int\n }\n \n+type bigIntStruct {\n+\tA *big.Int\n+}\n+\n type nestedStruct {\n \tA intStruct\n }\n @@ -65,6 +70,7 @@ var marshalTests = []marshalTest{\n \t{-128, "020180"},\n \t{-129, "0202ff7f"},\n \t{intStruct{64}, "3003020140"},\n+\t{bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},\n \t{twoIntStruct{64, 65}, "3006020140020141"},\n \t{nestedStruct{intStruct{127}}, "3005300302017f"},\n \t{[]byte{1, 2, 3}, "0403010203"},\n```
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0448ce13a0bd69b9a81e9a259b9b9bd9b58c70d6
元コミット内容
encoding/asn1: document support for *big.Int
Also add basic tests.
変更の背景
このコミットの背景には、Go言語の encoding/asn1
パッケージが既に math/big
パッケージの *big.Int
型を内部的にサポートしていたにもかかわらず、その事実が公式ドキュメントに明記されていなかったという状況があります。また、この重要な機能に対するテストカバレッジが不足していたことも問題でした。
ASN.1 (Abstract Syntax Notation One) は、データ構造を記述するための標準であり、特にネットワークプロトコルやセキュリティ関連のアプリケーションで広く利用されます。ASN.1 の INTEGER
型は、非常に大きな整数値を表現できるため、暗号化やデジタル署名などの分野で不可欠です。Go言語の標準ライブラリである encoding/asn1
は、Goの構造体とASN.1のデータ構造間のマーシャリング(GoのデータからASN.1バイト列への変換)およびアンマーシャリング(ASN.1バイト列からGoのデータへの変換)を提供します。
Goの組み込み整数型(int
, int32
, int64
)は、そのサイズに上限があります。しかし、ASN.1の INTEGER
は任意精度をサポートするため、これらの組み込み型では表現しきれない非常に大きな整数値が存在します。このようなケースに対応するため、Goには任意精度整数を扱う math/big
パッケージが提供されており、その中の *big.Int
型が大きな整数値を扱うための標準的な方法となっています。
このコミット以前は、encoding/asn1
が *big.Int
を扱えることは、コードを深く読み込むか、経験的に知る必要がありました。これは、開発者にとって不便であり、誤解を招く可能性がありました。また、テストがないということは、将来の変更によってこの機能が意図せず壊れてしまうリスクがあることを意味します。
したがって、このコミットは以下の目的で実施されました。
- ドキュメントの明確化:
encoding/asn1
が*big.Int
をサポートしていることを公式ドキュメントに明記し、開発者がこの機能を利用しやすくする。 - テストカバレッジの向上:
*big.Int
のマーシャリングとアンマーシャリングが正しく機能することを保証する基本的なテストケースを追加し、コードの堅牢性を高める。
これにより、encoding/asn1
パッケージの使いやすさと信頼性が向上し、特に大きな整数値を扱うASN.1ベースのプロトコルを実装する際の開発効率とコード品質が改善されます。
前提知識の解説
ASN.1 (Abstract Syntax Notation One)
ASN.1は、データ構造を記述するための国際標準(ITU-T X.680シリーズ)です。異なるシステム間でデータを交換する際に、データの表現方法を明確に定義し、プラットフォームやプログラミング言語に依存しない形でデータをシリアライズ(バイト列に変換)およびデシリアライズ(バイト列からデータ構造に変換)することを可能にします。
ASN.1は、以下のような特徴を持ちます。
- 抽象構文: データの論理的な構造を定義します。例えば、「このデータは整数である」「このデータは文字列のリストである」といった形で定義します。
- 符号化規則 (Encoding Rules): 抽象構文で定義されたデータを、実際にバイト列に変換するための具体的な規則です。代表的なものに、BER (Basic Encoding Rules)、DER (Distinguished Encoding Rules)、CER (Canonical Encoding Rules)、PER (Packed Encoding Rules)、XER (XML Encoding Rules)、JER (JSON Encoding Rules) などがあります。Goの
encoding/asn1
パッケージは主にDERを扱います。 - 型システム:
INTEGER
,BOOLEAN
,OCTET STRING
,SEQUENCE
,SET
,ENUMERATED
など、様々な組み込み型と、それらを組み合わせて複雑なデータ構造を定義する機能を提供します。
ASN.1は、X.509デジタル証明書、LDAP、SNMP、S/MIME、そして多くの暗号プロトコルなど、セキュリティやネットワーク通信の分野で広く利用されています。
Go言語の encoding/asn1
パッケージ
encoding/asn1
パッケージは、Go言語でASN.1データをエンコード(マーシャリング)およびデコード(アンマーシャリング)するための標準ライブラリです。このパッケージは、Goの構造体とASN.1のデータ構造をマッピングすることで、開発者がASN.1の複雑なバイト列操作を直接行うことなく、Goの型システムを通じてASN.1データを扱うことを可能にします。
主な機能は以下の通りです。
Marshal(v interface{}) ([]byte, error)
: Goの値をASN.1 DER形式のバイト列に変換します。Unmarshal(b []byte, v interface{}) ([]byte, error)
: ASN.1 DER形式のバイト列をGoの値に変換します。- 構造体のフィールドタグ (
asn1:"..."
) を使用して、ASN.1のタグ、型、オプション(例:optional
,default
,set
)などを指定できます。
このパッケージは、特にX.509証明書やPKCS#1などの標準的な暗号関連のデータ構造を扱う際に非常に有用です。
Go言語の math/big
パッケージと *big.Int
math/big
パッケージは、Go言語で任意精度の数値を扱うためのパッケージです。Goの組み込み整数型(int
, int8
, int16
, int32
, int64
, uint
, uint8
, uint16
, uint32
, uint64
)は、固定のビット幅を持ち、表現できる数値の範囲に制限があります。しかし、暗号学的な計算や非常に大きな数値を扱う必要がある場合(例: RSA公開鍵のモジュラスや指数、楕円曲線暗号の座標など)、これらの組み込み型では対応できません。
math/big
パッケージは、以下の主要な型を提供します。
*big.Int
: 任意精度の整数を表現します。*big.Float
: 任意精度の浮動小数点数を表現します。*big.Rat
: 任意精度の有理数を表現します。
このコミットで関連するのは *big.Int
です。*big.Int
は、メモリが許す限り任意の大きさの整数を表現できます。ASN.1の INTEGER
型は任意精度をサポートするため、GoでASN.1の INTEGER
を扱う際には、その値が組み込み整数型に収まらない場合に *big.Int
を使用することが適切かつ必要になります。
*big.Int
の値はポインタとして扱われることが一般的です(例: *big.Int
)。これは、大きな数値のコピーを避けるためと、メソッドがレシーバの値を変更できるようにするためです。
Go言語の reflect
パッケージ
reflect
パッケージは、Go言語の実行時に型情報を検査し、値を動的に操作するための機能を提供します。encoding/asn1
のような汎用的なエンコーディング/デコーディングパッケージは、事前に具体的なGoの型を知ることができません。そのため、reflect
パッケージを使用して、与えられたGoのインターフェース値の型や構造を調べ、それに基づいてASN.1のバイト列との間でデータの変換を行います。
Unmarshal
関数は、reflect.Value
を使用して、入力されたバイト列をGoの構造体の適切なフィールドにマッピングします。同様に、Marshal
関数は reflect.Value
を使用して、Goの構造体のフィールドから値を取得し、ASN.1バイト列を構築します。
このコミットの変更箇所である asn1.go
のコメントにも「Because Unmarshal uses the reflect package, the structs being written to must use upper case field names.」とあるように、reflect
パッケージの動作原理(エクスポートされたフィールドのみがリフレクションでアクセス可能)が encoding/asn1
の設計に影響を与えています。
技術的詳細
このコミットは、encoding/asn1
パッケージがASN.1の INTEGER
型をGoの *big.Int
型にマッピングできるという既存の機能を、ドキュメントとテストによって公式化するものです。
ASN.1の INTEGER
型は、その名の通り整数値を表現しますが、その値の範囲は符号化規則によって異なります。DER (Distinguished Encoding Rules) では、INTEGER
は最小限のバイト数で符号化され、先頭バイトが0x00または0xFFで始まる場合は、値の符号を明確にするために追加のバイトが挿入されることがあります。重要なのは、ASN.1の INTEGER
は理論上、任意精度の整数を表現できるという点です。
Goの encoding/asn1
パッケージは、ASN.1の INTEGER
をGoの組み込み整数型(int
, int32
, int64
)にアンマーシャリングする機能を持っていました。しかし、これらの型は固定のビット幅を持つため、ASN.1の INTEGER
が非常に大きな値を持つ場合、オーバーフローが発生する可能性があります。このような状況を避けるため、Goの math/big
パッケージが提供する *big.Int
型が、任意精度の整数を安全に扱うための解決策となります。
このコミットが行われる前も、encoding/asn1
は内部的に *big.Int
を処理するロジックを持っていたと考えられます。しかし、その機能が明示的にドキュメント化されていなかったため、開発者はその存在を知らずに、あるいはその動作を信頼せずに使用していた可能性があります。また、テストがないことは、その機能が将来の変更によって意図せず壊れるリスクを伴いました。
このコミットの技術的なポイントは以下の通りです。
-
ドキュメントの更新:
src/pkg/encoding/asn1/asn1.go
のコメントが更新され、Unmarshal
関数がASN.1のINTEGER
をint
,int32
,int64
に加えて*big.Int
にもデコードできることが明記されました。これにより、開発者はこの機能を安心して利用できるようになります。// An ASN.1 INTEGER can be written to an int, int32, int64, // or *big.Int (from the math/big package). // If the encoded value does not fit in the Go type, // Unmarshal returns a parse error.
このコメントは、
Unmarshal
がGoの型に値をデコードする際の挙動を説明しており、特に*big.Int
がサポートされることを強調しています。もしASN.1の整数値がGoのターゲット型(int
,int32
,int64
)に収まらない場合、Unmarshal
はパースエラーを返すという重要な注意点も含まれています。*big.Int
の場合は、任意精度であるため、通常はオーバーフローの問題は発生しません。 -
アンマーシャリングテストの追加:
src/pkg/encoding/asn1/asn1_test.go
に、*big.Int
を含む構造体をアンマーシャリングするためのテストケースが追加されました。TestBigInt
という新しい構造体が定義され、*big.Int
型のフィールドX
を持ちます。unmarshalTestData
スライスに、ASN.1バイト列0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56
と、それに対応するTestBigInt
型の期待値(big.NewInt(0x123456)
)が追加されました。0x30, 0x05
: SEQUENCE (タグ 0x30), 長さ 5バイト0x02, 0x03
: INTEGER (タグ 0x02), 長さ 3バイト0x12, 0x34, 0x56
: 整数値 0x123456 (1193046)
-
マーシャリングテストの追加:
src/pkg/encoding/asn1/marshal_test.go
に、*big.Int
を含む構造体をマーシャリングするためのテストケースが追加されました。bigIntStruct
という新しい構造体が定義され、*big.Int
型のフィールドA
を持ちます。marshalTests
スライスに、bigIntStruct
型の入力値(big.NewInt(0x123456)
)と、それに対応する期待されるASN.1バイト列の16進数表現30050203123456
が追加されました。これはアンマーシャリングテストで使用されたバイト列と同じです。
これらの変更により、encoding/asn1
パッケージの *big.Int
サポートが公式に認識され、その機能が将来にわたって維持されるための基本的な保証が提供されました。
コアとなるコードの変更箇所
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
index 22a0dde0da..4d1ae38c4e 100644
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -786,7 +786,8 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
// Because Unmarshal uses the reflect package, the structs
// being written to must use upper case field names.
//
-// An ASN.1 INTEGER can be written to an int, int32 or int64.\n+// An ASN.1 INTEGER can be written to an int, int32, int64,\n+// or *big.Int (from the math/big package).\n // If the encoded value does not fit in the Go type,\n // Unmarshal returns a parse error.\n //\ndiff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
index 09f94139f9..92c9eb62d2 100644
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -6,6 +6,7 @@ package asn1
import (
"bytes"
+ "math/big"
"reflect"
"testing"
"time"
@@ -351,6 +352,10 @@ type TestElementsAfterString struct {
A, B int
}
+type TestBigInt struct {
+ X *big.Int
+}
+
var unmarshalTestData = []struct {
in []byte
out interface{}
@@ -369,6 +374,7 @@ var unmarshalTestData = []struct {
{[]byte{0x01, 0x01, 0x00}, newBool(false)},
{[]byte{0x01, 0x01, 0x01}, 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)}},\n }\n
func TestUnmarshal(t *testing.T) {
diff --git a/src/pkg/encoding/asn1/marshal_test.go b/src/pkg/encoding/asn1/marshal_test.go
index d05b5d8d4e..a7447f9781 100644
--- a/src/pkg/encoding/asn1/marshal_test.go
+++ b/src/pkg/encoding/asn1/marshal_test.go
@@ -7,6 +7,7 @@ package asn1
import (
"bytes"
"encoding/hex"
+ "math/big"
"testing"
"time"
)
@@ -20,6 +21,10 @@ type twoIntStruct struct {
B int
}
+type bigIntStruct {
+ A *big.Int
+}
+
type nestedStruct {
A intStruct
}
@@ -65,6 +70,7 @@ var marshalTests = []marshalTest{\n {-128, "020180"},\n {-129, "0202ff7f"},\n {intStruct{64}, "3003020140"},\n + {bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},\n {twoIntStruct{64, 65}, "3006020140020141"},\n {nestedStruct{intStruct{127}}, "3005300302017f"},\n {[]byte{1, 2, 3}, "0403010203"},\n```
## コアとなるコードの解説
### `src/pkg/encoding/asn1/asn1.go` の変更
このファイルでは、`Unmarshal` 関数のドキュメンテーションコメントが更新されています。
```diff
- // An ASN.1 INTEGER can be written to an int, int32 or int64.\n+ // An ASN.1 INTEGER can be written to an int, int32, int64,\n+ // or *big.Int (from the math/big package).\n```
変更前は、ASN.1の `INTEGER` 型がGoの `int`, `int32`, `int64` にデコードできるとだけ記述されていました。変更後は、これらに加えて `math/big` パッケージの `*big.Int` 型にもデコードできることが明記されました。これは、`encoding/asn1` パッケージが任意精度の整数を扱う能力を持つことを公式に宣言するものであり、開発者にとって非常に重要な情報です。これにより、大きな整数値を扱うASN.1データ構造をGoで安全に処理できることが明確になります。
### `src/pkg/encoding/asn1/asn1_test.go` の変更
このファイルでは、`*big.Int` 型のアンマーシャリングをテストするためのコードが追加されています。
1. **`math/big` パッケージのインポート**:
```diff
+ "math/big"
```
`*big.Int` 型を使用するために、`math/big` パッケージがインポートされました。
2. **`TestBigInt` 構造体の追加**:
```go
type TestBigInt struct {
X *big.Int
}
```
この構造体は、ASN.1の `INTEGER` を `*big.Int` 型のフィールド `X` にアンマーシャリングするテストケースのターゲットとして使用されます。
3. **`unmarshalTestData` へのテストケースの追加**:
```diff
+ {[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}},\n
```
`unmarshalTestData` は、`TestUnmarshal` 関数によって実行されるアンマーシャリングテストのデータセットです。追加されたエントリは以下の内容を示します。
* `[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}`: これはASN.1 DER形式のバイト列です。
* `0x30`: SEQUENCEタグ
* `0x05`: 長さ5バイト
* `0x02`: INTEGERタグ
* `0x03`: 長さ3バイト
* `0x12, 0x34, 0x56`: 整数値 `0x123456` (10進数で1193046) を表すバイト列
* `&TestBigInt{big.NewInt(0x123456)}`: 上記のバイト列がアンマーシャリングされたときに期待されるGoの構造体です。`big.NewInt(0x123456)` は、指定された整数値を持つ新しい `*big.Int` オブジェクトを作成します。
このテストケースは、ASN.1の `INTEGER` が含まれるSEQUENCEを、Goの `*big.Int` フィールドを持つ構造体に正しくアンマーシャリングできることを検証します。
### `src/pkg/encoding/asn1/marshal_test.go` の変更
このファイルでは、`*big.Int` 型のマーシャリングをテストするためのコードが追加されています。
1. **`math/big` パッケージのインポート**:
```diff
+ "math/big"
```
`*big.Int` 型を使用するために、`math/big` パッケージがインポートされました。
2. **`bigIntStruct` 構造体の追加**:
```go
type bigIntStruct struct {
A *big.Int
}
```
この構造体は、`*big.Int` 型のフィールド `A` を持ち、マーシャリングテストの入力として使用されます。
3. **`marshalTests` へのテストケースの追加**:
```diff
+ {bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},\n
```
`marshalTests` は、`TestMarshal` 関数によって実行されるマーシャリングテストのデータセットです。追加されたエントリは以下の内容を示します。
* `bigIntStruct{big.NewInt(0x123456)}`: マーシャリングされるGoの構造体です。`big.NewInt(0x123456)` は、整数値 `0x123456` を持つ `*big.Int` オブジェクトです。
* `"30050203123456"`: 上記のGoの構造体がマーシャリングされたときに期待されるASN.1 DER形式のバイト列の16進数表現です。これはアンマーシャリングテストで使用されたバイト列と一致します。
このテストケースは、Goの `*big.Int` フィールドを持つ構造体が、対応するASN.1の `INTEGER` を含むSEQUENCEに正しくマーシャリングできることを検証します。
これらのテストの追加により、`encoding/asn1` パッケージが `*big.Int` を用いたASN.1の `INTEGER` のマーシャリングおよびアンマーシャリングを正しく処理できることが保証され、この機能の信頼性が向上しました。
## 関連リンク
* Go言語 `encoding/asn1` パッケージのドキュメント: [https://pkg.go.dev/encoding/asn1](https://pkg.go.dev/encoding/asn1)
* Go言語 `math/big` パッケージのドキュメント: [https://pkg.go.dev/math/big](https://pkg.go.dev/math/big)
* ASN.1 (Abstract Syntax Notation One) - Wikipedia: [https://ja.wikipedia.org/wiki/ASN.1](https://ja.wikipedia.org/wiki/ASN.1)
## 参考にした情報源リンク
* Go言語の公式ドキュメント (`encoding/asn1` および `math/big` パッケージ)
* ASN.1に関する一般的な知識 (Wikipediaなど)
* コミットの差分情報 (`git diff`)
* Go言語のテストの書き方に関する一般的な知識