🔐
【Go】Goで学ぶ暗号技術 ~OFBモード~
はじめに
最近暗号技術について興味があり、少しずつ学習をしています。
第1回はECBモードを、第2回はCBCモードを、第3回はCFBモードを取り上げました。
今回はOFBモードを紹介します。
※今回の実装では、非推奨となっているメソッドを利用しています。あらかじめご了承ください。
第1回
第2回 第3回✅この記事でわかること
- OFBモードの特徴
- OFBモードの仕組み(暗号化・復号化の流れ)
- OFBモードを使う上での注意点
📝OFBモードの特徴
- Output Feedbackの略
- CBCモードやCFBモード同様、IV(初期化ベクトル)を使って暗号化する
- IVを暗号化し続けるイメージ
📊OFBモード図解
暗号化
- 最初は暗号化したIVと平文をXOR
- 暗号化されたIVをさらに暗号化し平文とXOR
復号化
- 最初は暗号化したIVと暗号ブロックをXOR
- 暗号化されたIVをさらに暗号化し暗号ブロックとXOR
⚠️OFBモードの注意点
- パディング不要だが、IVの管理は重要
→暗号文にIVを付加して一緒に保存・送信するのが一般的 - 鍵とIVは16バイト(AESブロックサイズ)で固定長
- Go 1.24では
crypto/cipher
パッケージのNewOFB
は非推奨に
// コメントアウト引用
Deprecated: OFB mode is not authenticated, which generally enables active
attacks to manipulate and recover the plaintext. It is recommended that
applications use [AEAD] modes instead. The standard library implementation of
OFB is also unoptimized and not validated as part of the FIPS 140-3 module.
If an unauthenticated [Stream] mode is required, use [NewCTR] instead.
非推奨の理由
- OFBモードには認証機能がないため、改竄されても検知できない
→AEADモード(認証付き暗号 / Authenticated Encryption with Associated Data)を使う
→GCMモードが適している - 実装が最適化されておらず、FIPS認証もない
→認証されていなくても使いたい場合はNewCTR
を使うこと
🛠Goでの実装方法
暗号化の手順
- ランダムなIVを生成
-
NewOFB
で暗号化ストリームを作成 -
XORKeyStream
で平文を暗号化
復号化の手順
-
NewOFB
で復号ストリームを作成 -
XORKeyStream
で復号化
実装
main.go
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"log"
)
// OFB暗号化
func encryptOFB(key, plainText []byte) ([]byte, []byte, error) {
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
return nil, nil, fmt.Errorf("invalid Key Length: %d bytes", len(key))
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, err
}
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, nil, err
}
encryptText := make([]byte, len(plainText))
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(encryptText, plainText)
return encryptText, iv, nil
}
// OFB復号化
func decryptedOFB(key, encryptedText, iv []byte) ([]byte, error) {
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
return nil, fmt.Errorf("invalid Key length: %d bytes", len(key))
}
if len(iv) != aes.BlockSize {
return nil, fmt.Errorf("invalid IV length: got %d bytes, want %d bytes", len(iv), aes.BlockSize)
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
decryptedText := make([]byte, len(encryptedText))
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(decryptedText, encryptedText)
return decryptedText, nil
}
func main() {
key := []byte("thisisofbexample")
plainText := []byte("Hello, OFB mode!")
fmt.Println("plainText", string(plainText))
encryptedText, iv, err := encryptOFB(key, plainText)
if err != nil {
log.Fatal(err)
}
fmt.Println("EncryptedText:", hex.EncodeToString(encryptedText))
fmt.Println("IV:", hex.EncodeToString(iv))
decryptedText, err := decryptedOFB(key, encryptedText, iv)
if err != nil {
log.Fatal(err)
}
fmt.Println("DecryptedText:", string(decryptedText))
}
出力結果例
ターミナル
plainText: Hello, OFB mode!
EncryptedText: e35c27382c041701da18ea795d817f03
IV: e75fb0a542ba014f44f0c27f85595c21
DecryptedText: Hello, OFB mode!
🗒️まとめ
今回はOFBモードについて書いていきました。
CFBモードと類似している点が多いと感じる一方で、どこで暗号化するかという部分が異なるということを学びました。
また、Go 1.24にて非推奨になったことで、別の暗号利用モードやその関数を用いる必要がありそうということも知ることができました。
次回は、CTRモードについて書いていこうと思います。
📕参考
Discussion