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

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

このコミットは、Go言語の標準ライブラリ encoding/base32 および encoding/base64 パッケージにおけるデコード処理の堅牢性を向上させるものです。特に、不正なパディングを持つ入力文字列が与えられた際に発生しうるパニック(プログラムの異常終了)を防ぎ、より適切なエラーハンドリングを行うように修正されています。

コミット

commit 79d06d7286eb5ec3089f19dbf8542803007cd4c7
Author: Nigel Tao <nigeltao@golang.org>
Date:   Tue Mar 12 11:07:36 2013 +1100

    encoding/base32: don't panic when decoding "AAAA==".
    
    Edit encoding/base64's internals and tests to match encoding/base32.
    
    Properly handling line breaks in padding is left for another CL.
    
    R=dsymonds
    CC=golang-dev
    https://golang.org/cl/7693044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/79d06d7286eb5ec3089f19dbf8542803007cd4c7

元コミット内容

encoding/base32: "AAAA==" をデコードする際にパニックを起こさないようにする。

encoding/base32 の内部実装とテストを encoding/base64 に合わせて修正する。

パディング内の改行の適切な処理は、別の変更リスト(CL)に持ち越される。

変更の背景

Base32およびBase64エンコーディングは、バイナリデータをASCII文字列に変換するための一般的な手法です。これらのエンコーディングでは、元のバイナリデータの長さがエンコーディングのブロックサイズ(Base32では5バイト、Base64では3バイト)の倍数でない場合、出力文字列の末尾にパディング文字(=)が追加されます。

このコミットが行われる前は、encoding/base32 パッケージのデコード処理において、不正なパディング(例えば、Base32エンコーディングのルールに反する数の = が付与された文字列)が与えられた場合に、プログラムがパニックを起こす可能性がありました。具体的には、コミットメッセージに記載されている "AAAA==" のような入力がその一例です。これは、デコーダが予期しない入力形式に遭遇した際に、適切なエラーハンドリングが行われず、プログラムがクラッシュしてしまうことを意味します。

この問題は、デコード処理の堅牢性に関わる重要なバグであり、不正な入力に対してもパニックではなく、明確なエラー(CorruptInputError)を返すように修正する必要がありました。また、Base32とBase64は類似のエンコーディングスキームであるため、Base32で発見されたこの問題の修正をBase64にも適用し、両パッケージの一貫性と信頼性を高めることが目的とされました。

前提知識の解説

Base32エンコーディング

Base32は、バイナリデータを32種類のASCII文字(A-Zと2-7)で表現するエンコーディング方式です。主に、人間が読みやすく、かつURLやファイル名に安全に含めることができる形式として利用されます。

  • エンコーディングの仕組み: 5ビットのデータを1文字で表現します。元のバイナリデータが5バイト(40ビット)の場合、8文字のBase32文字列にエンコードされます。
  • パディング: 元のバイナリデータのビット数が40ビットの倍数でない場合、出力の末尾にパディング文字 = が追加されます。RFC 4648では、Base32のパディングの長さは0, 1, 3, 4, 6のいずれかであると規定されています。例えば、1バイト(8ビット)のデータは2文字のBase32と6つの = でパディングされます(例: AE======)。

Base64エンコーディング

Base64は、バイナリデータを64種類のASCII文字(A-Z, a-z, 0-9, +, /)で表現するエンコーディング方式です。電子メールの添付ファイルやHTTPのBasic認証など、バイナリデータをテキスト形式で安全に転送する際によく用いられます。

  • エンコーディングの仕組み: 6ビットのデータを1文字で表現します。元のバイナリデータが3バイト(24ビット)の場合、4文字のBase64文字列にエンコードされます。
  • パディング: 元のバイナリデータのビット数が24ビットの倍数でない場合、出力の末尾にパディング文字 = が追加されます。Base64のパディングの長さは0, 1, 2のいずれかです。

パニックとエラーハンドリング

Go言語において、パニック (panic) はプログラムの回復不可能なエラーを示すメカニズムです。パニックが発生すると、通常のプログラムフローは中断され、スタックトレースが出力されてプログラムが終了します。これは、予期せぬ状態やプログラマの想定外の事態が発生した場合に用いられます。

一方、エラーハンドリング (error handling) は、予期されるが正常な処理ではない状況(例: ファイルが見つからない、ネットワーク接続が切れた、不正な入力)を適切に処理するための仕組みです。Goでは、関数がエラーを返すことで、呼び出し元がそのエラーを検知し、適切な回復処理を行うことができます。

このコミットの目的は、不正な入力(特にパディング)に対してパニックではなく、CorruptInputError という特定のエラーを返すようにすることで、ライブラリの利用者がエラーを捕捉し、適切に処理できるようにすることです。

RFC 4648

RFC 4648は、Base16, Base32, Base64エンコーディングの標準を定義したIETFの文書です。このRFCには、各エンコーディング方式のアルファベット、エンコード/デコードのアルゴリズム、そしてパディングのルールが詳細に記述されています。このコミットでは、特にBase32のパディングルール(セクション6および9)に厳密に従うようにデコードロジックが修正されています。

技術的詳細

このコミットの主要な変更点は、encoding/base32 および encoding/base64 パッケージのデコード関数 (decode メソッド) におけるパディング処理のロジック強化です。

Base32の変更点

src/pkg/encoding/base32/base32.godecode 関数において、パディング文字 (=) の処理がより厳密になりました。

  1. パディングの長さチェックの強化:
    • 以前は、パディング文字が見つかった際に、残りの入力がすべて = であることを確認するだけでした。
    • 修正後は、if len(src)+j < 8-1 という条件が追加され、パディング文字の前に十分なデータが存在するかどうかをチェックします。これは「パディングが足りない」状況を検出します。
    • また、for k := 0; k < 8-1-j; k++ ループ内で if len(src) > k && src[k] != '=' というチェックが追加され、パディング文字の後に不正な文字が含まれていないか(「不正なパディング」)を確認します。
  2. 不正なパディング長の検出:
    • 最も重要な変更点の一つは、dlen == 1 || dlen == 3 || dlen == 6 という条件が追加されたことです。
    • Base32エンコーディングの仕様(RFC 4648 Section 6)では、パディングによってデコードされるデータブロックの長さ (dlen) が1、3、6になることはありません。これらの値は、入力が不正であることを示します。
    • この修正により、例えば "A=" (dlen=1), "AAA=" (dlen=3), "AAAAAA=" (dlen=6) のような不正なパディングを持つ入力に対して、即座に CorruptInputError を返すようになりました。これにより、デコード処理が途中でパニックを起こすことを防ぎます。
  3. デコード量子(quantum)処理の修正:
    • switch dlen のケースが調整されました。例えば、以前は case 7, 8: だったものが case 8: に、case 6, 5:case 7: に変更されています。これは、RFC 4648で定義されているBase32のデコード量子(8文字のBase32入力が何バイトのバイナリデータに変換されるか)に厳密に合わせるための修正です。これにより、デコードされるバイト数が正確に計算されるようになります。

Base64の変更点

src/pkg/encoding/base64/base64.godecode 関数にも、Base32と同様のパディング処理の堅牢化が適用されました。

  1. パディングの長さチェックの強化:
    • if len(src)+j < 4-1 という条件が追加され、Base64のブロックサイズ(4文字)に基づいてパディングの不足を検出します。
    • Base64では、Base32のような特定の dlen 値の不正チェックは明示的に追加されていませんが、これはBase64のパディングルールがBase32よりも単純であるためと考えられます。

テストの変更点

両パッケージのテストファイル (base32_test.go, base64_test.go) の TestDecodeCorrupt 関数が大幅に拡張されました。

  • corrupt 構造体が testCases スライスに変更され、input 文字列と期待される offset (エラーが発生する位置)を持つようになりました。
  • offset == -1 という新しい値が導入され、これは入力が有効であり、エラーが発生しないことを示すために使用されます。
  • これにより、様々な有効な入力と、意図的に不正なパディングを持つ入力(例: "A=", "AA=", "AA==", "AAAA=", "AAAA==" など)が追加され、デコーダが正しく CorruptInputError を返すか、またはエラーを返さないかを検証できるようになりました。これにより、修正が正しく機能していることが保証されます。

コアとなるコードの変更箇所

src/pkg/encoding/base32/base32.go

--- a/src/pkg/encoding/base32/base32.go
+++ b/src/pkg/encoding/base32/base32.go
@@ -236,7 +236,6 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 		var dbuf [8]byte
 		dlen := 8
 
-		// do the top bytes contain any data?
 		for j := 0; j < 8; {
 			if len(src) == 0 {
 				return n, false, CorruptInputError(len(osrc) - len(src) - j)
@@ -248,15 +247,26 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 				continue
 			}
 			if in == '=' && j >= 2 && len(src) < 8 {
-				// We've reached the end and there's
-				// padding, the rest should be padded
-				for k := 0; k < 8-j-1; k++ {
+				// We've reached the end and there's padding
+				if len(src)+j < 8-1 {
+					// not enough padding
+					return n, false, CorruptInputError(len(osrc))
+				}
+				for k := 0; k < 8-1-j; k++ {
 					if len(src) > k && src[k] != '=' {
+						// incorrect padding
 						return n, false, CorruptInputError(len(osrc) - len(src) + k - 1)
 					}
 				}
-				dlen = j
-				end = true
+				dlen, end = j, true
+				// 7, 5 and 2 are not valid padding lengths, and so 1, 3 and 6 are not
+				// valid dlen values. See RFC 4648 Section 6 "Base 32 Encoding" listing
+				// the five valid padding lengths, and Section 9 "Illustrations and
+				// Examples" for an illustration for how the the 1st, 3rd and 6th base32
+				// src bytes do not yield enough information to decode a dst byte.
+				if dlen == 1 || dlen == 3 || dlen == 6 {
+					return n, false, CorruptInputError(len(osrc) - len(src) - 1)
+				}
 				break
 			}
 			dbuf[j] = enc.decodeMap[in]
@@ -269,16 +279,16 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 		// Pack 8x 5-bit source blocks into 5 byte destination
 		// quantum
 		switch dlen {
-		case 7, 8:
+		case 8:
 			dst[4] = dbuf[6]<<5 | dbuf[7]
 			fallthrough
-		case 6, 5:
+		case 7:
 			dst[3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3
 			fallthrough
-		case 4:
+		case 5:
 			dst[2] = dbuf[3]<<4 | dbuf[4]>>1
 			fallthrough
-		case 3:
+		case 4:
 			dst[1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4
 			fallthrough
 		case 2:
@@ -288,7 +298,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 		switch dlen {
 		case 2:
 			n += 1
-		case 3, 4:
+		case 4:
 			n += 2
 		case 5:
 			n += 3
-		case 6, 7:
+		case 7:
 			n += 4
 		case 8:
 			n += 5

src/pkg/encoding/base64/base64.go

--- a/src/pkg/encoding/base64/base64.go
+++ b/src/pkg/encoding/base64/base64.go
@@ -227,9 +227,8 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 				continue
 			}
 			if in == '=' && j >= 2 && len(src) < 4 {
-				// We've reached the end and there's
-				// padding
-				if len(src) == 0 && j == 2 {
+				// We've reached the end and there's padding
+				if len(src)+j < 4-1 {
 					// not enough padding
 					return n, false, CorruptInputError(len(osrc))
 				}
@@ -237,8 +236,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 				\t// incorrect padding
 				\treturn n, false, CorruptInputError(len(osrc) - len(src) - 1)
 				}
-				dlen = j
-				end = true
+				dlen, end = j, true
 				break
 			}
 			dbuf[j] = enc.decodeMap[in]

コアとなるコードの解説

上記のコード変更は、Base32およびBase64デコーダが不正なパディングを持つ入力に遭遇した際の挙動を改善するために行われました。

encoding/base32/base32.go の変更点詳細

  • パディングチェックの強化:
    • if in == '=' && j >= 2 && len(src) < 8 ブロックは、パディング文字 (=) が検出された際の処理です。
    • if len(src)+j < 8-1 は、現在の入力バッファ src の残りの長さと、既に処理した文字数 j を合計しても、Base32の1ブロック(8文字)を構成するのに必要な文字数(パディング文字を除く)に満たない場合に、パディングが不足していると判断し、CorruptInputError を返します。
    • for k := 0; k < 8-1-j; k++ { ... } ループは、パディング文字の後に続く文字がすべて = であることを確認します。もし = 以外の文字があれば、それは不正なパディングであり、CorruptInputError を返します。
  • 不正な dlen の検出:
    • if dlen == 1 || dlen == 3 || dlen == 6 の行が追加されました。dlen は、デコードされたバイナリデータの有効なバイト数を示します。RFC 4648のBase32仕様では、8文字のBase32入力からデコードされるバイナリデータのバイト数は、0, 2, 4, 5のいずれかになります。したがって、dlen が1, 3, 6になることはありえず、これらは不正な入力であることを示します。このチェックにより、仕様に準拠しない入力に対して早期にエラーを返すことができます。
  • dlenend の同時代入:
    • dlen, end = j, true は、Goの多値代入を利用して、dlenend の両方を一度に設定しています。これは機能的な変更ではなく、コードの簡潔化です。
  • switch dlen のケース修正:
    • case 7, 8:case 8: に、case 6, 5:case 7: に、case 4:case 5: に、case 3:case 4: に、case 3, 4:case 4: に、case 6, 7:case 7: に変更されました。これらの変更は、Base32のデコードロジックがRFC 4648の仕様に厳密に準拠するように、デコードされるバイト数と入力文字数の関係を正確にマッピングするためのものです。これにより、例えば7文字のBase32入力が4バイトのバイナリデータにデコードされるといった、正しい量子変換が行われるようになります。

encoding/base64/base64.go の変更点詳細

  • パディングチェックの強化:
    • if in == '=' && j >= 2 && len(src) < 4 ブロックは、Base64のパディング文字 (=) が検出された際の処理です。
    • if len(src)+j < 4-1 は、Base64の1ブロック(4文字)を構成するのに必要な文字数に満たない場合に、パディングが不足していると判断し、CorruptInputError を返します。
    • dlen, end = j, true は、Base32と同様に多値代入による簡潔化です。

これらの変更により、デコーダは不正なパディングを持つ入力に対してパニックを起こすことなく、CorruptInputError を返すようになり、ライブラリの堅牢性と信頼性が向上しました。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語の標準ライブラリ encoding/base32 および encoding/base64 パッケージにおけるデコード処理の堅牢性を向上させるものです。特に、不正なパディングを持つ入力文字列が与えられた際に発生しうるパニック(プログラムの異常終了)を防ぎ、より適切なエラーハンドリングを行うように修正されています。

コミット

commit 79d06d7286eb5ec3089f19dbf8542803007cd4c7
Author: Nigel Tao <nigeltao@golang.org>
Date:   Tue Mar 12 11:07:36 2013 +1100

    encoding/base32: don't panic when decoding "AAAA==".
    
    Edit encoding/base64's internals and tests to match encoding/base32.
    
    Properly handling line breaks in padding is left for another CL.
    
    R=dsymonds
    CC=golang-dev
    https://golang.org/cl/7693044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/79d06d7286eb5ec3089f19dbf8542803007cd4c7

元コミット内容

encoding/base32: "AAAA==" をデコードする際にパニックを起こさないようにする。

encoding/base32 の内部実装とテストを encoding/base64 に合わせて修正する。

パディング内の改行の適切な処理は、別の変更リスト(CL)に持ち越される。

変更の背景

Base32およびBase64エンコーディングは、バイナリデータをASCII文字列に変換するための一般的な手法です。これらのエンコーディングでは、元のバイナリデータの長さがエンコーディングのブロックサイズ(Base32では5バイト、Base64では3バイト)の倍数でない場合、出力文字列の末尾にパディング文字(=)が追加されます。

このコミットが行われる前は、encoding/base32 パッケージのデコード処理において、不正なパディング(例えば、Base32エンコーディングのルールに反する数の = が付与された文字列)が与えられた場合に、プログラムがパニックを起こす可能性がありました。具体的には、コミットメッセージに記載されている "AAAA==" のような入力がその一例です。これは、デコーダが予期しない入力形式に遭遇した際に、適切なエラーハンドリングが行われず、プログラムがクラッシュしてしまうことを意味します。

この問題は、デコード処理の堅牢性に関わる重要なバグであり、不正な入力に対してもパニックではなく、明確なエラー(CorruptInputError)を返すように修正する必要がありました。また、Base32とBase64は類似のエンコーディングスキームであるため、Base32で発見されたこの問題の修正をBase64にも適用し、両パッケージの一貫性と信頼性を高めることが目的とされました。

前提知識の解説

Base32エンコーディング

Base32は、バイナリデータを32種類のASCII文字(A-Zと2-7)で表現するエンコーディング方式です。主に、人間が読みやすく、かつURLやファイル名に安全に含めることができる形式として利用されます。

  • エンコーディングの仕組み: 5ビットのデータを1文字で表現します。元のバイナリデータが5バイト(40ビット)の場合、8文字のBase32文字列にエンコードされます。
  • パディング: 元のバイナリデータのビット数が40ビットの倍数でない場合、出力の末尾にパディング文字 = が追加されます。RFC 4648では、Base32のパディングの長さは0, 1, 3, 4, 6のいずれかであると規定されています。例えば、1バイト(8ビット)のデータは2文字のBase32と6つの = でパディングされます(例: AE======)。

Base64エンコーディング

Base64は、バイナリデータを64種類のASCII文字(A-Z, a-z, 0-9, +, /)で表現するエンコーディング方式です。電子メールの添付ファイルやHTTPのBasic認証など、バイナリデータをテキスト形式で安全に転送する際によく用いられます。

  • エンコーディングの仕組み: 6ビットのデータを1文字で表現します。元のバイナリデータが3バイト(24ビット)の場合、4文字のBase64文字列にエンコードされます。
  • パディング: 元のバイナリデータのビット数が24ビットの倍数でない場合、出力の末尾にパディング文字 = が追加されます。Base64のパディングの長さは0, 1, 2のいずれかです。

パニックとエラーハンドリング

Go言語において、パニック (panic) はプログラムの回復不可能なエラーを示すメカニズムです。パニックが発生すると、通常のプログラムフローは中断され、スタックトレースが出力されてプログラムが終了します。これは、予期せぬ状態やプログラマの想定外の事態が発生した場合に用いられます。

一方、エラーハンドリング (error handling) は、予期されるが正常な処理ではない状況(例: ファイルが見つからない、ネットワーク接続が切れた、不正な入力)を適切に処理するための仕組みです。Goでは、関数がエラーを返すことで、呼び出し元がそのエラーを検知し、適切な回復処理を行うことができます。

このコミットの目的は、不正な入力(特にパディング)に対してパニックではなく、CorruptInputError という特定のエラーを返すようにすることで、ライブラリの利用者がエラーを捕捉し、適切に処理できるようにすることです。

RFC 4648

RFC 4648は、Base16, Base32, Base64エンコーディングの標準を定義したIETFの文書です。このRFCには、各エンコーディング方式のアルファベット、エンコード/デコードのアルゴリズム、そしてパディングのルールが詳細に記述されています。このコミットでは、特にBase32のパディングルール(セクション6および9)に厳密に従うようにデコードロジックが修正されています。

技術的詳細

このコミットの主要な変更点は、encoding/base32 および encoding/base64 パッケージのデコード関数 (decode メソッド) におけるパディング処理のロジック強化です。

Base32の変更点

src/pkg/encoding/base32/base32.godecode 関数において、パディング文字 (=) の処理がより厳密になりました。

  1. パディングの長さチェックの強化:
    • 以前は、パディング文字が見つかった際に、残りの入力がすべて = であることを確認するだけでした。
    • 修正後は、if len(src)+j < 8-1 という条件が追加され、パディング文字の前に十分なデータが存在するかどうかをチェックします。これは「パディングが足りない」状況を検出します。
    • また、for k := 0; k < 8-1-j; k++ ループ内で if len(src) > k && src[k] != '=' というチェックが追加され、パディング文字の後に不正な文字が含まれていないか(「不正なパディング」)を確認します。
  2. 不正なパディング長の検出:
    • 最も重要な変更点の一つは、dlen == 1 || dlen == 3 || dlen == 6 という条件が追加されたことです。
    • Base32エンコーディングの仕様(RFC 4648 Section 6)では、パディングによってデコードされるデータブロックの長さ (dlen) が1、3、6になることはありません。これらの値は、入力が不正であることを示します。
    • この修正により、例えば "A=" (dlen=1), "AAA=" (dlen=3), "AAAAAA=" (dlen=6) のような不正なパディングを持つ入力に対して、即座に CorruptInputError を返すようになりました。これにより、デコード処理が途中でパニックを起こすことを防ぎます。
  3. デコード量子(quantum)処理の修正:
    • switch dlen のケースが調整されました。例えば、以前は case 7, 8: だったものが case 8: に、case 6, 5:case 7: に変更されています。これは、RFC 4648で定義されているBase32のデコード量子(8文字のBase32入力が何バイトのバイナリデータに変換されるか)に厳密に合わせるための修正です。これにより、デコードされるバイト数が正確に計算されるようになります。

Base64の変更点

src/pkg/encoding/base64/base64.godecode 関数にも、Base32と同様のパディング処理の堅牢化が適用されました。

  1. パディングの長さチェックの強化:
    • if len(src)+j < 4-1 という条件が追加され、Base64のブロックサイズ(4文字)に基づいてパディングの不足を検出します。
    • Base64では、Base32のような特定の dlen 値の不正チェックは明示的に追加されていませんが、これはBase64のパディングルールがBase32よりも単純であるためと考えられます。

テストの変更点

両パッケージのテストファイル (base32_test.go, base64_test.go) の TestDecodeCorrupt 関数が大幅に拡張されました。

  • corrupt 構造体が testCases スライスに変更され、input 文字列と期待される offset (エラーが発生する位置)を持つようになりました。
  • offset == -1 という新しい値が導入され、これは入力が有効であり、エラーが発生しないことを示すために使用されます。
  • これにより、様々な有効な入力と、意図的に不正なパディングを持つ入力(例: "A=", "AA=", "AA==", "AAAA=", "AAAA==" など)が追加され、デコーダが正しく CorruptInputError を返すか、またはエラーを返さないかを検証できるようになりました。これにより、修正が正しく機能していることが保証されます。

コアとなるコードの変更箇所

src/pkg/encoding/base32/base32.go

--- a/src/pkg/encoding/base32/base32.go
+++ b/src/pkg/encoding/base32/base32.go
@@ -236,7 +236,6 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 		var dbuf [8]byte
 		dlen := 8
 
-		// do the top bytes contain any data?
 		for j := 0; j < 8; {
 			if len(src) == 0 {
 				return n, false, CorruptInputError(len(osrc) - len(src) - j)
@@ -248,15 +247,26 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 				continue
 			}
 			if in == '=' && j >= 2 && len(src) < 8 {
-				// We've reached the end and there's
-				// padding, the rest should be padded
-				for k := 0; k < 8-j-1; k++ {
+				// We've reached the end and there's padding
+				if len(src)+j < 8-1 {
+					// not enough padding
+					return n, false, CorruptInputError(len(osrc))
+				}
+				for k := 0; k < 8-1-j; k++ {
 					if len(src) > k && src[k] != '=' {
+						// incorrect padding
 						return n, false, CorruptInputError(len(osrc) - len(src) + k - 1)
 					}
 				}
-				dlen = j
-				end = true
+				dlen, end = j, true
+				// 7, 5 and 2 are not valid padding lengths, and so 1, 3 and 6 are not
+				// valid dlen values. See RFC 4648 Section 6 "Base 32 Encoding" listing
+				// the five valid padding lengths, and Section 9 "Illustrations and
+				// Examples" for an illustration for how the the 1st, 3rd and 6th base32
+				// src bytes do not yield enough information to decode a dst byte.
+				if dlen == 1 || dlen == 3 || dlen == 6 {
+					return n, false, CorruptInputError(len(osrc) - len(src) - 1)
+				}
 				break
 			}
 			dbuf[j] = enc.decodeMap[in]
@@ -269,16 +289,16 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 		// Pack 8x 5-bit source blocks into 5 byte destination
 		// quantum
 		switch dlen {
-		case 7, 8:
+		case 8:
 			dst[4] = dbuf[6]<<5 | dbuf[7]
 			fallthrough
-		case 6, 5:
+		case 7:
 			dst[3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3
 			fallthrough
-		case 4:
+		case 5:
 			dst[2] = dbuf[3]<<4 | dbuf[4]>>1
 			fallthrough
-		case 3:
+		case 4:
 			dst[1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4
 			fallthrough
 		case 2:
@@ -288,7 +308,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 		switch dlen {
 		case 2:
 			n += 1
-		case 3, 4:
+		case 4:
 			n += 2
 		case 5:
 			n += 3
-		case 6, 7:
+		case 7:
 			n += 4
 		case 8:
 			n += 5

src/pkg/encoding/base64/base64.go

--- a/src/pkg/encoding/base64/base64.go
+++ b/src/pkg/encoding/base64/base64.go
@@ -227,9 +227,8 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 				continue
 			}
 			if in == '=' && j >= 2 && len(src) < 4 {
-				// We've reached the end and there's
-				// padding
-				if len(src) == 0 && j == 2 {
+				// We've reached the end and there's padding
+				if len(src)+j < 4-1 {
 					// not enough padding
 					return n, false, CorruptInputError(len(osrc))
 				}
@@ -237,8 +236,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
 				\t// incorrect padding
 				\treturn n, false, CorruptInputError(len(osrc) - len(src) - 1)
 				}
-				dlen = j
-				end = true
+				dlen, end = j, true
 				break
 			}
 			dbuf[j] = enc.decodeMap[in]

コアとなるコードの解説

上記のコード変更は、Base32およびBase64デコーダが不正なパディングを持つ入力に遭遇した際の挙動を改善するために行われました。

encoding/base32/base32.go の変更点詳細

  • パディングチェックの強化:
    • if in == '=' && j >= 2 && len(src) < 8 ブロックは、パディング文字 (=) が検出された際の処理です。
    • if len(src)+j < 8-1 は、現在の入力バッファ src の残りの長さと、既に処理した文字数 j を合計しても、Base32の1ブロック(8文字)を構成するのに必要な文字数(パディング文字を除く)に満たない場合に、パディングが不足していると判断し、CorruptInputError を返します。
    • for k := 0; k < 8-1-j; k++ { ... } ループは、パディング文字の後に続く文字がすべて = であることを確認します。もし = 以外の文字があれば、それは不正なパディングであり、CorruptInputError を返します。
  • 不正な dlen の検出:
    • if dlen == 1 || dlen == 3 || dlen == 6 の行が追加されました。dlen は、デコードされたバイナリデータの有効なバイト数を示します。RFC 4648のBase32仕様では、8文字のBase32入力からデコードされるバイナリデータのバイト数は、0, 2, 4, 5のいずれかになります。したがって、dlen が1, 3, 6になることはありえず、これらは不正な入力であることを示します。このチェックにより、仕様に準拠しない入力に対して早期にエラーを返すことができます。
  • dlenend の同時代入:
    • dlen, end = j, true は、Goの多値代入を利用して、dlenend の両方を一度に設定しています。これは機能的な変更ではなく、コードの簡潔化です。
  • switch dlen のケース修正:
    • case 7, 8:case 8: に、case 6, 5:case 7: に、case 4:case 5: に、case 3:case 4: に、case 3, 4:case 4: に、case 6, 7:case 7: に変更されました。これらの変更は、Base32のデコードロジックがRFC 4648の仕様に厳密に準拠するように、デコードされるバイト数と入力文字数の関係を正確にマッピングするためのものです。これにより、例えば7文字のBase32入力が4バイトのバイナリデータにデコードされるといった、正しい量子変換が行われるようになります。

encoding/base64/base64.go の変更点詳細

  • パディングチェックの強化:
    • if in == '=' && j >= 2 && len(src) < 4 ブロックは、Base64のパディング文字 (=) が検出された際の処理です。
    • if len(src)+j < 4-1 は、Base64の1ブロック(4文字)を構成するのに必要な文字数に満たない場合に、パディングが不足していると判断し、CorruptInputError を返します。
    • dlen, end = j, true は、Base32と同様に多値代入による簡潔化です。

これらの変更により、デコーダは不正なパディングを持つ入力に対してパニックを起こすことなく、CorruptInputError を返すようになり、ライブラリの堅牢性と信頼性が向上しました。

関連リンク

参考にした情報源リンク