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