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

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

このコミットは、Go言語の encoding/asn1 パッケージにおける *big.Int 型のサポートに関するドキュメントを更新し、その基本的なテストケースを追加するものです。具体的には、ASN.1 の INTEGER 型が intint32int64 に加えて 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 型のアンマーシャリングをテストするためのコードが追加されています。

  1. math/big パッケージのインポート:

    +	"math/big"
    

    *big.Int 型を使用するために、math/big パッケージがインポートされました。

  2. TestBigInt 構造体の追加:

    type TestBigInt struct {
    	X *big.Int
    }
    

    この構造体は、ASN.1の INTEGER*big.Int 型のフィールド X にアンマーシャリングするテストケースのターゲットとして使用されます。

  3. 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 型のマーシャリングをテストするためのコードが追加されています。

  1. math/big パッケージのインポート:

    +	"math/big"
    

    *big.Int 型を使用するために、math/big パッケージがインポートされました。

  2. bigIntStruct 構造体の追加:

    type bigIntStruct struct {
    	A *big.Int
    }
    

    この構造体は、*big.Int 型のフィールド A を持ち、マーシャリングテストの入力として使用されます。

  3. 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 および 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, and math/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 を扱えることは、コードを深く読み込むか、経験的に知る必要がありました。これは、開発者にとって不便であり、誤解を招く可能性がありました。また、テストがないということは、将来の変更によってこの機能が意図せず壊れてしまうリスクがあることを意味します。

したがって、このコミットは以下の目的で実施されました。

  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の INTEGERint, 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 の場合は、任意精度であるため、通常はオーバーフローの問題は発生しません。

  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 --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言語のテストの書き方に関する一般的な知識