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

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

このコミットは、Go言語の標準ライブラリ crypto/cipher パッケージに、暗号化モードの具体的な使用例を示す example_test.go ファイルを追加するものです。このファイルは、GoのテストフレームワークのExample機能を利用して、NewCBCDecrypter, NewCBCEncrypter, NewCFBDecrypter, NewCFBEncrypter, NewCTR, NewOFB, StreamReader, StreamWriter といった主要な関数や型の使い方を実演しています。これにより、開発者が crypto/cipher パッケージを安全かつ正確に利用するための手助けとなります。

コミット

commit 5176481f16d2e7f32df41924582a0a36073f7063
Author: Adam Langley <agl@golang.org>
Date:   Wed Oct 31 16:37:26 2012 -0400

    crypto/cipher: add examples
    
    Fixes #1390.
    
    R=golang-dev, minux.ma, adg, agl
    CC=golang-dev
    https://golang.org/cl/6631044

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

https://github.com/golang/go/commit/5176481f16d2e7f32df41924582a0a36073f7063

元コミット内容

crypto/cipher パッケージに例を追加。 Issue #1390 を修正。

変更の背景

この変更の背景には、Go言語の crypto/cipher パッケージが提供する暗号化機能の利用方法が、当時のドキュメントだけでは十分に明確でなかったという課題がありました。特に、ブロック暗号のモード(CBC, CFB, CTR, OFBなど)は、初期化ベクトル(IV)の扱い、パディングの必要性、そして最も重要な「認証」の欠如といった、セキュリティ上非常に重要な側面を伴います。

Issue #1390("crypto/cipher: add examples")は、まさにこの問題意識から提起されたものです。ユーザーが crypto/cipher パッケージを誤って使用し、意図せず脆弱な実装をしてしまうリスクを減らすため、具体的なコード例を提供することが求められました。これにより、開発者は単にAPIの呼び出し方を知るだけでなく、安全な暗号化処理を構築するためのベストプラクティス(特に認証の重要性)を理解できるようになります。

前提知識の解説

このコミットの変更内容を深く理解するためには、以下の暗号技術に関する前提知識が必要です。

1. 対称鍵暗号 (Symmetric-key Cryptography)

暗号化と復号に同じ鍵を使用する暗号方式です。AES (Advanced Encryption Standard) は、最も広く使われている対称鍵ブロック暗号の一つです。

2. ブロック暗号 (Block Cipher)

データを固定長のブロック(例: AESでは128ビット)に分割し、ブロックごとに暗号化・復号を行う暗号アルゴリズムです。ブロック暗号単体では、同じ平文ブロックが同じ暗号文ブロックになるため、パターンが露呈する可能性があります。これを防ぐために、様々な「モード」が考案されています。

3. 暗号利用モード (Modes of Operation)

ブロック暗号を連続したデータに適用するための方法です。各モードには異なる特性とセキュリティ上の考慮事項があります。

  • CBC (Cipher Block Chaining):

    • 各平文ブロックが、前の暗号文ブロックとXORされてから暗号化されます。
    • 最初のブロックには初期化ベクトル (IV) が必要です。
    • 同じ平文ブロックでも、前のブロックやIVが異なれば異なる暗号文になります。
    • 平文の長さがブロックサイズの倍数でない場合、パディングが必要です。
    • 復号時には、暗号文ブロックを復号し、前の暗号文ブロック(またはIV)とXORします。
    • 重要な注意点: CBCモードは、パディングオラクル攻撃に対して脆弱になる可能性があります。そのため、暗号文は復号前に必ず認証される必要があります。
  • CFB (Cipher Feedback):

    • ブロック暗号をストリーム暗号のように動作させます。
    • 前の暗号文ブロック(またはIV)を暗号化し、その結果と平文ブロックをXORして暗号文を生成します。
    • 平文の長さがブロックサイズの倍数でなくても動作します(パディング不要)。
    • 暗号化と復号で同じロジックを使用できます。
  • CTR (Counter Mode):

    • ブロック暗号をストリーム暗号のように動作させます。
    • カウンタとIVを組み合わせた値をブロック暗号で暗号化し、その結果(キーストリーム)と平文をXORして暗号文を生成します。
    • 各ブロックの暗号化は独立しており、並列処理が可能です。
    • 平文の長さがブロックサイズの倍数でなくても動作します(パディング不要)。
    • 暗号化と復号で同じロジックを使用できます。
    • 重要な注意点: 同じIVとカウンタの組み合わせを二度と使用してはなりません。
  • OFB (Output Feedback):

    • ブロック暗号をストリーム暗号のように動作させます。
    • IVをブロック暗号で暗号化し、その出力を次の入力としてブロック暗号に与え、キーストリームを生成します。このキーストリームと平文をXORして暗号文を生成します。
    • 平文の長さがブロックサイズの倍数でなくても動作します(パディング不要)。
    • 暗号化と復号で同じロジックを使用できます。

4. 初期化ベクトル (IV: Initialization Vector)

暗号利用モードで使用される、ランダムまたは擬似ランダムな値です。

  • ユニーク性: 各暗号化操作でユニークである必要があります。
  • 機密性: IV自体は機密である必要はありません(通常、暗号文の先頭に付加して送信されます)。
  • 目的: 同じ鍵と平文でも異なる暗号文を生成し、パターン攻撃を防ぎます。

5. パディング (Padding)

ブロック暗号では、平文の長さがブロックサイズの倍数である必要があります。そうでない場合、平文の末尾に特定のバイト列を追加して長さを調整します。復号時にはこのパディングを取り除きます。RFC 5246のセクション6.2.3.2などで定義されています。

6. 認証 (Authentication) と HMAC (Hash-based Message Authentication Code)

暗号化だけではデータの改ざんを防ぐことはできません。 攻撃者が暗号文を改ざんし、復号された平文に影響を与える可能性があります(例: ビットフリップ攻撃)。これを防ぐために、暗号文の認証が必要です。

  • HMAC: メッセージ認証コードの一種で、秘密鍵とハッシュ関数を用いてメッセージの完全性と認証性を保証します。暗号化されたデータにHMACを付加し、復号前にHMACを検証することで、データが改ざんされていないことを確認します。
  • 重要性: このコミットの例では、繰り返し「暗号文は認証されなければならない」という警告がコメントされています。これは、暗号化と認証を組み合わせる「Authenticated Encryption」の原則に従うことの重要性を示しています。Go言語では crypto/hmac パッケージや crypto/cipher/aes.NewGCM (GCMモードは認証付き暗号) などが利用できます。

技術的詳細

このコミットで追加された example_test.go ファイルは、Goの testing パッケージのExample機能を利用しています。Example 関数は、その関数名が Example で始まり、かつ Output: コメントを持つ場合、go test 実行時にその出力が検証されます。これにより、コード例が常に正しく動作し、期待される出力を生成することが保証されます。

ファイル内で示されている主要な技術的ポイントは以下の通りです。

  1. crypto/aescrypto/cipher の連携:

    • aes.NewCipher(key): AESブロック暗号のインスタンスを作成します。これは cipher.Block インターフェースを実装しています。
    • cipher.NewCBCEncrypter(block, iv) / cipher.NewCBCDecrypter(block, iv): cipher.Block インターフェースとIVを受け取り、CBCモードのエンクリプター/デクリプターを返します。これらは cipher.BlockMode インターフェースを実装しています。
    • cipher.NewCFBEncrypter(block, iv) / cipher.NewCFBDecrypter(block, iv): CFBモードのエンクリプター/デクリプターを返します。これらは cipher.Stream インターフェースを実装しています。
    • cipher.NewCTR(block, iv): CTRモードのストリーム暗号を返します。これは cipher.Stream インターフェースを実装しています。
    • cipher.NewOFB(block, iv): OFBモードのストリーム暗号を返します。これは cipher.Stream インターフェースを実装しています。
  2. 初期化ベクトル (IV) の生成と扱い:

    • ほとんどの例で、IVは crypto/rand.Reader を使用して安全に生成されています (io.ReadFull(rand.Reader, iv)).
    • IVは機密である必要はないため、暗号文の先頭に付加して送信されるのが一般的です。例では、ciphertext := make([]byte, aes.BlockSize+len(plaintext)) のように、暗号文バッファの先頭にIVのためのスペースを確保しています。
  3. パディングの考慮:

    • CBCモードの例では、平文がブロックサイズの倍数である必要があることが明示されています。そうでない場合はパディングが必要であり、RFC 5246が参照されています。
    • ストリーム暗号モード(CFB, CTR, OFB)ではパディングが不要であることが示唆されています。
  4. 認証の重要性:

    • すべての暗号化例において、「暗号文は認証されなければならない(例: crypto/hmac を使用して)」というセキュリティ上の重要な警告がコメントとして含まれています。これは、暗号化だけでは改ざんを防げないという暗号の基本原則を強調しています。
  5. StreamReaderStreamWriter:

    • これらは io.Readerio.Writer インターフェースを実装しており、ストリーム暗号(cipher.Stream)と組み合わせて、ファイルなどのI/Oストリームを透過的に暗号化/復号するのに使用できます。
    • これらの例でも、認証の欠如が攻撃を許す可能性があるという警告がされています。

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

追加されたファイルは src/pkg/crypto/cipher/example_test.go です。 このファイルは283行からなり、以下のExample関数を含んでいます。

  • ExampleNewCBCDecrypter()
  • ExampleNewCBCEncrypter()
  • ExampleNewCFBDecrypter()
  • ExampleNewCFBEncrypter()
  • ExampleNewCTR()
  • ExampleNewOFB()
  • ExampleStreamReader()
  • ExampleStreamWriter()

コアとなるコードの解説

ExampleNewCBCDecrypter()

  • 目的: CBCモードでの復号の例。
  • 処理:
    1. 鍵と、IVが先頭に付加された暗号文(hex.DecodeString でデコード)を準備。
    2. aes.NewCipher でAESブロックを作成。
    3. 暗号文からIVを抽出し、残りを実際の暗号文とする。
    4. cipher.NewCBCDecrypter でCBCデクリプターを作成。
    5. mode.CryptBlocks(ciphertext, ciphertext) でインプレース復号。
    6. 復号された平文を出力。
  • セキュリティ警告: パディングの必要性と、パディングオラクル攻撃を防ぐための認証の重要性がコメントで強調されています。

ExampleNewCBCEncrypter()

  • 目的: CBCモードでの暗号化の例。
  • 処理:
    1. 鍵と平文を準備。平文がブロックサイズの倍数であることを前提。
    2. aes.NewCipher でAESブロックを作成。
    3. 暗号文バッファをIVと暗号文のために確保。
    4. crypto/rand.Reader を使ってIVを安全に生成し、暗号文バッファの先頭に格納。
    5. cipher.NewCBCEncrypter でCBCエンクリプターを作成。
    6. mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext) で暗号化。
    7. 暗号文を16進数で出力。
  • セキュリティ警告: 暗号文が認証されることの重要性がコメントで強調されています。

ExampleNewCFBDecrypter()

  • 目的: CFBモードでの復号の例。
  • 処理:
    1. 鍵と、IVが先頭に付加された暗号文を準備。
    2. aes.NewCipher でAESブロックを作成。
    3. 暗号文からIVを抽出し、残りを実際の暗号文とする。
    4. cipher.NewCFBDecrypter でCFBデクリプターを作成。
    5. stream.XORKeyStream(ciphertext, ciphertext) でインプレース復号。
    6. 復号された平文を出力。

ExampleNewCFBEncrypter()

  • 目的: CFBモードでの暗号化の例。
  • 処理:
    1. 鍵と平文を準備。
    2. aes.NewCipher でAESブロックを作成。
    3. 暗号文バッファをIVと暗号文のために確保。
    4. crypto/rand.Reader を使ってIVを安全に生成し、暗号文バッファの先頭に格納。
    5. cipher.NewCFBEncrypter でCFBエンクリプターを作成。
    6. stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext) で暗号化。
  • セキュリティ警告: 暗号文が認証されることの重要性がコメントで強調されています。

ExampleNewCTR()

  • 目的: CTRモードでの暗号化と復号の例。
  • 処理:
    1. 鍵と平文を準備。
    2. aes.NewCipher でAESブロックを作成。
    3. 暗号文バッファをIVと暗号文のために確保。
    4. crypto/rand.Reader を使ってIVを安全に生成し、暗号文バッファの先頭に格納。
    5. cipher.NewCTR でCTRストリームを作成し、暗号化。
    6. CTRモードは暗号化と復号で同じロジックを使用するため、同じ NewCTR を使って暗号文を復号できることを示す。
    7. 復号された平文を出力。
  • セキュリティ警告: 暗号文が認証されることの重要性がコメントで強調されています。

ExampleNewOFB()

  • 目的: OFBモードでの暗号化と復号の例。
  • 処理:
    1. 鍵と平文を準備。
    2. aes.NewCipher でAESブロックを作成。
    3. 暗号文バッファをIVと暗号文のために確保。
    4. crypto/rand.Reader を使ってIVを安全に生成し、暗号文バッファの先頭に格納。
    5. cipher.NewOFB でOFBストリームを作成し、暗号化。
    6. OFBモードは暗号化と復号で同じロジックを使用するため、同じ NewOFB を使って暗号文を復号できることを示す。
    7. 復号された平文を出力。
  • セキュリティ警告: 暗号文が認証されることの重要性がコメントで強調されています。

ExampleStreamReader()

  • 目的: cipher.StreamReader を使用してファイルから読み込みながら復号する例。
  • 処理:
    1. 鍵を準備。
    2. 暗号化されたファイル (encrypted-file) を開く(例では存在しないためエラーになる可能性あり)。
    3. aes.NewCipher でAESブロックを作成。
    4. IVをゼロバイト配列として準備(鍵が各暗号文でユニークな場合にのみ安全)。
    5. cipher.NewOFB でOFBストリームを作成。
    6. 復号されたデータを書き込むファイル (decrypted-file) を作成。
    7. cipher.StreamReader を作成し、io.Copy で入力ファイルから出力ファイルへコピーしながら復号。
  • セキュリティ警告: この例は認証を省略しており、攻撃者が出力の任意のビットを反転させることができる(ビットフリップ攻撃)と警告しています。

ExampleStreamWriter()

  • 目的: cipher.StreamWriter を使用してファイルに書き込みながら暗号化する例。
  • 処理:
    1. 鍵を準備。
    2. 平文ファイル (plaintext-file) を開く(例では存在しないためエラーになる可能性あり)。
    3. aes.NewCipher でAESブロックを作成。
    4. IVをゼロバイト配列として準備(鍵が各暗号文でユニークな場合にのみ安全)。
    5. cipher.NewOFB でOFBストリームを作成。
    6. 暗号化されたデータを書き込むファイル (encrypted-file) を作成。
    7. cipher.StreamWriter を作成し、io.Copy で入力ファイルから出力ファイルへコピーしながら暗号化。
  • セキュリティ警告: この例は認証を省略しており、攻撃者が復号された結果の任意のビットを反転させることができると警告しています。

関連リンク

参考にした情報源リンク