[インデックス 16402] ファイルの概要
このコミットは、Go言語の encoding/asn1 パッケージにおけるDER (Distinguished Encoding Rules) エンコードされたブーリアン値のパース処理をより厳密にするための変更です。具体的には、X.690標準に準拠し、ブーリアン値としてエンコードが許容されるのは 0 (FALSE) と 255 (TRUE) のオクテットのみであることを強制するよう修正されました。また、この変更を検証するための新しいテストケースが追加されています。
コミット
commit af48543c5458086435c92f63677723217900c1b5
Author: Gerasimos Dimitriadis <gedimitr@gmail.com>
Date: Fri May 24 12:37:42 2013 -0400
asn1: Stricter checks for DER encoded booleans
According to X.690, only 0 and 255 are allowed as values
for encoded booleans. Also added some test for parsing
booleans
R=golang-dev, agl, r
CC=golang-dev
https://golang.org/cl/9692043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/af48543c5458086435c92f63677723217900c1b5
元コミット内容
asn1: Stricter checks for DER encoded booleans
According to X.690, only 0 and 255 are allowed as values
for encoded booleans. Also added some test for parsing
booleans
変更の背景
この変更の背景には、ASN.1 (Abstract Syntax Notation One) のエンコーディングルールの一つであるDER (Distinguished Encoding Rules) の厳密な要件があります。以前の実装では、ブーリアン値のエンコードにおいて、単一オクテットであれば 0 以外は全て true と解釈されていました。しかし、X.690標準(ASN.1エンコーディング仕様)のDERセクションでは、ブーリアン値のエンコードは非常に厳密に定義されており、FALSE は 0x00、TRUE は 0xFF (全てのビットが1) の単一オクテットでなければならないと規定されています。
このコミットは、Goの encoding/asn1 パッケージがこのDERの厳密な要件に準拠するように修正することを目的としています。これにより、不正な形式のブーリアンエンコーディングがパースされた場合にエラーを返すようになり、より堅牢で標準に準拠したASN.1パーサーが提供されます。
前提知識の解説
ASN.1 (Abstract Syntax Notation One)
ASN.1は、データ構造を記述するための標準的な記法です。異なるシステム間でデータを交換する際に、データの表現方法を抽象的に定義するために使用されます。これにより、プラットフォームやプログラミング言語に依存しないデータ交換が可能になります。ASN.1自体はデータの「構造」を定義するものであり、その構造をバイト列に変換する具体的な「エンコーディングルール」は別途定義されます。
DER (Distinguished Encoding Rules)
DERは、ASN.1で定義されたデータ構造をバイト列にエンコードするためのエンコーディングルールの一つです。DERの最大の特徴は、一意性 (uniqueness) です。つまり、特定のASN.1データ構造に対して、DERエンコーディングは常にただ一つのバイト列に決定されます。これは、デジタル署名やハッシュ計算など、データの表現が一意であることが求められるセキュリティ関連のプロトコルで特に重要です。
DERは、BER (Basic Encoding Rules) のサブセットであり、BERが許容する複数のエンコーディング形式の中から、特定の形式のみを許可することで一意性を保証します。
ASN.1におけるブーリアン値のエンコーディング (DER)
X.690標準のDERセクションでは、ブーリアン値のエンコーディングについて以下のように規定されています。
- タグ: ブーリアン型はユニバーサルタグ
BOOLEAN (1)を持ちます。 - 長さ: ブーリアン値のエンコードは常に1オクテットの長さです。
- 値:
FALSEの場合、値オクテットは0x00(全てのビットが0) でなければなりません。TRUEの場合、値オクテットは0xFF(全てのビットが1) でなければなりません。
これ以外の値(例えば 0x01 や 0x7F など)は、DERエンコードされたブーリアンとしては不正と見なされます。
技術的詳細
このコミットは、主に src/pkg/encoding/asn1/asn1.go ファイル内の parseBool 関数と、それに対応するテストファイル src/pkg/encoding/asn1/asn1_test.go に変更を加えています。
parseBool 関数の変更
変更前は、parseBool 関数はブーリアン値のバイト列の長さが1オクテットであることを確認した後、そのオクテットが 0 でなければ true と解釈していました。これはBERの一般的な解釈には合致しますが、DERの厳密な要件には準拠していませんでした。
変更後、parseBool 関数は以下のチェックを追加しています。
- 長さのチェック: 引き続き、入力バイト列の長さが1オクテットであることを確認します。これに違反する場合は
SyntaxErrorを返します。 - 値の厳密なチェック:
switchステートメントを使用して、単一のオクテットが0x00または0xFFであるかを厳密にチェックします。0x00の場合はfalseを返します。0xFFの場合はtrueを返します。- これら以外の値(例:
0x01)の場合は、SyntaxError{"encoding/asn1: invalid boolean"}を返してエラーとします。
これにより、DERの仕様に厳密に準拠したブーリアンパースが実現されます。
テストケースの追加と修正
src/pkg/encoding/asn1/asn1_test.go には、TestParseBool という新しいテスト関数が追加されました。このテストは、boolTestData という構造化されたテストデータを使用して、parseBool 関数の新しい厳密な挙動を検証します。
boolTestData には、以下のケースが含まれています。
{[]byte{0x00}, true, false}: 正しいFALSEエンコーディング。{[]byte{0xff}, true, true}: 正しいTRUEエンコーディング。{[]byte{0x00, 0x00}, false, false}: 長さが不正なケース(エラーが期待される)。{[]byte{0xff, 0xff}, false, false}: 長さが不正なケース(エラーが期待される)。{[]byte{0x01}, false, false}: DERでは不正なTRUEエンコーディング(エラーが期待される)。
また、既存の unmarshalTestData の一部も修正されています。特に、{[]byte{0x01, 0x01, 0x01}, newBool(true)} というテストケースが {[]byte{0x01, 0x01, 0xff}, newBool(true)} に変更され、TRUE のエンコーディングが 0x01 から 0xff に修正されています。これは、Unmarshal 関数が内部で parseBool を呼び出すため、その挙動がDERに準拠するように整合性を保つための変更です。
コアとなるコードの変更箇所
diff --git a/src/pkg/encoding/asn1/asn1.go b/src/pkg/encoding/asn1/asn1.go
old mode 100644
new mode 100755
index cac9d64b5e..a14df04eff
--- a/src/pkg/encoding/asn1/asn1.go
+++ b/src/pkg/encoding/asn1/asn1.go
@@ -47,11 +47,23 @@ func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg }
func parseBool(bytes []byte) (ret bool, err error) {
if len(bytes) != 1 {
- err = SyntaxError{"invalid boolean"}
+ err = SyntaxError{"encoding/asn1: invalid boolean"}
return
}
- return bytes[0] != 0, nil
+ // DER demands that "If the encoding represents the boolean value TRUE,
+ // its single contents octet shall have all eight bits set to one."
+ // Thus only 0 and 255 are valid encoded values.
+ switch bytes[0] {
+ case 0:
+ ret = false
+ case 0xff:
+ ret = true
+ default:
+ err = SyntaxError{"encoding/asn1: invalid boolean"}
+ }
+
+ return
}
// INTEGER
diff --git a/src/pkg/encoding/asn1/asn1_test.go b/src/pkg/encoding/asn1/asn1_test.go
old mode 100644
new mode 100755
index 6e98dcf0b9..fb82937b7e
--- a/src/pkg/encoding/asn1/asn1_test.go
+++ b/src/pkg/encoding/asn1/asn1_test.go
@@ -12,6 +12,32 @@ import (
"time"
)
+type boolTest struct {
+ in []byte
+ ok bool
+ out bool
+}
+
+var boolTestData = []boolTest{
+ {[]byte{0x00}, true, false},
+ {[]byte{0xff}, true, true},
+ {[]byte{0x00, 0x00}, false, false},
+ {[]byte{0xff, 0xff}, false, false},
+ {[]byte{0x01}, false, false},
+}
+
+func TestParseBool(t *testing.T) {
+ for i, test := range boolTestData {
+ ret, err := parseBool(test.in)
+ if (err == nil) != test.ok {
+ t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
+ }
+ if test.ok && ret != test.out {
+ t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
+ }
+ }
+}
+
type int64Test struct {
in []byte
ok bool
@@ -378,7 +404,7 @@ var unmarshalTestData = []struct {
{[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}},
{[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}},
{[]byte{0x01, 0x01, 0x00}, newBool(false)},
- {[]byte{0x01, 0x01, 0x01}, newBool(true)},
+ {[]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)}},
}
コアとなるコードの解説
src/pkg/encoding/asn1/asn1.go の parseBool 関数
func parseBool(bytes []byte) (ret bool, err error) {
if len(bytes) != 1 {
err = SyntaxError{"encoding/asn1: invalid boolean"}
return
}
// DER demands that "If the encoding represents the boolean value TRUE,
// its single contents octet shall have all eight bits set to one."
// Thus only 0 and 255 are valid encoded values.
switch bytes[0] {
case 0:
ret = false
case 0xff:
ret = true
default:
err = SyntaxError{"encoding/asn1: invalid boolean"}
}
return
}
この関数は、ASN.1のブーリアン値を表すバイトスライス bytes を受け取り、Goの bool 型とエラーを返します。
if len(bytes) != 1: まず、入力されたバイトスライスの長さが1オクテットであるかをチェックします。DERではブーリアン値は常に1オクテットでエンコードされるため、これ以外の長さは不正です。不正な場合はSyntaxErrorを設定して関数を終了します。エラーメッセージもより具体的になりました ("encoding/asn1: invalid boolean")。- コメント: DERの仕様に関する重要なコメントが追加されています。「エンコーディングがブーリアン値TRUEを表す場合、その単一のコンテンツオクテットは8ビット全てが1に設定されていなければならない。」と明記し、
0と255(0xFF) のみが有効なエンコード値であることを強調しています。 switch bytes[0]: 入力バイトスライスの最初の(唯一の)オクテットの値に基づいて処理を分岐します。case 0:: オクテットが0x00の場合、retをfalseに設定します。case 0xff:: オクテットが0xFFの場合、retをtrueに設定します。default:: 上記のいずれでもない場合(例:0x01など)、DERの仕様に違反するため、SyntaxErrorを設定してエラーを返します。
この変更により、parseBool 関数はDERの厳密なブーリアンエンコーディングルールに準拠し、不正な形式のブーリアン値に対しては明確にエラーを返すようになりました。
src/pkg/encoding/asn1/asn1_test.go の変更
type boolTest struct {
in []byte
ok bool
out bool
}
var boolTestData = []boolTest{
{[]byte{0x00}, true, false},
{[]byte{0xff}, true, true},
{[]byte{0x00, 0x00}, false, false},
{[]byte{0xff, 0xff}, false, false},
{[]byte{0x01}, false, false},
}
func TestParseBool(t *testing.T) {
for i, test := range boolTestData {
ret, err := parseBool(test.in)
if (err == nil) != test.ok {
t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok)
}
if test.ok && ret != test.out {
t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out)
}
}
}
boolTest構造体: テストケースの入力 (inバイトスライス)、期待されるエラーの有無 (okブーリアン)、期待されるパース結果 (outブーリアン) を定義するヘルパ構造体です。boolTestData変数:boolTest型のスライスで、parseBool関数の様々な入力と期待される出力を定義しています。特に、長さが不正なケース (0x00, 0x00や0xff, 0xff) や、DERでは不正な値 (0x01) がエラーとなることを検証するケースが含まれています。TestParseBool関数:forループでboolTestDataの各テストケースを反復処理します。parseBoolを呼び出し、返されたret(結果のブーリアン) とerr(エラー) を取得します。if (err == nil) != test.ok: エラーの有無が期待通りであるかをチェックします。test.okがtrueならエラーがないことを、falseならエラーがあることを期待します。if test.ok && ret != test.out: エラーが発生しなかった場合 (test.okがtrue)、パース結果retが期待されるtest.outと一致するかをチェックします。
この新しいテストスイートは、parseBool 関数の厳密なDER準拠の挙動を包括的に検証します。
また、unmarshalTestData の修正は、{[]byte{0x01, 0x01, 0x01}, newBool(true)} から {[]byte{0x01, 0x01, 0xff}, newBool(true)} への変更です。これは、Unmarshal 関数が内部で parseBool を使用するため、TRUE のエンコーディングが 0x01 ではなく 0xff であるべきというDERの要件に合わせて、テストデータ自体を修正したものです。
関連リンク
参考にした情報源リンク
- ITU-T Recommendation X.690 (08/2015) - Information technology – ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER):
- 特に、Section 8.2 "Encoding of a boolean value" を参照。
- https://www.itu.int/rec/T-REC-X.690-201508-I/en
- ASN.1 (Wikipedia): https://en.wikipedia.org/wiki/ASN.1
- Distinguished Encoding Rules (Wikipedia): https://en.wikipedia.org/wiki/Distinguished_Encoding_Rules