🔐

【Go】Goで学ぶ暗号技術 ~OFBモード~

に公開

はじめに

最近暗号技術について興味があり、少しずつ学習をしています。
第1回はECBモードを、第2回はCBCモードを、第3回はCFBモードを取り上げました。
今回はOFBモードを紹介します。
※今回の実装では、非推奨となっているメソッドを利用しています。あらかじめご了承ください。

第1回
https://zenn.dev/tmyhrn/articles/65ad6aa32964a5
第2回
https://zenn.dev/tmyhrn/articles/19542a7057f1c6
第3回
https://zenn.dev/tmyhrn/articles/b7cee9a8951a5e

✅この記事でわかること

  • OFBモードの特徴
  • OFBモードの仕組み(暗号化・復号化の流れ)
  • OFBモードを使う上での注意点

📝OFBモードの特徴

  • Output Feedbackの略
  • CBCモードやCFBモード同様、IV(初期化ベクトル)を使って暗号化する
  • IVを暗号化し続けるイメージ

📊OFBモード図解

暗号化

  1. 最初は暗号化したIVと平文をXOR
  2. 暗号化されたIVをさらに暗号化し平文とXOR

復号化

  1. 最初は暗号化したIVと暗号ブロックをXOR
  2. 暗号化された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.

非推奨の理由

  1. OFBモードには認証機能がないため、改竄されても検知できない
    →AEADモード(認証付き暗号 / Authenticated Encryption with Associated Data)を使う
    →GCMモードが適している
  2. 実装が最適化されておらず、FIPS認証もない
    →認証されていなくても使いたい場合はNewCTRを使うこと

https://tip.golang.org/doc/go1.24#cryptocipherpkgcryptocipher

🛠Goでの実装方法

暗号化の手順

  1. ランダムなIVを生成
  2. NewOFBで暗号化ストリームを作成
  3. XORKeyStreamで平文を暗号化

復号化の手順

  1. NewOFBで復号ストリームを作成
  2. 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モードについて書いていこうと思います。

📕参考

https://pkg.go.dev/crypto/aes
https://pkg.go.dev/crypto/cipher
https://tip.golang.org/doc/go1.24#cryptocipherpkgcryptocipher
https://www.cs.shinshu-u.ac.jp/~okazaki/TECCRYP/SEC2/e3/e35.html
https://www.ochappa.net/posts/block-enc-mode
https://qiita.com/omiso/items/6082b765c1257b71985b#ofbモード
https://qiita.com/liveasnotes/items/a5e35419242883029e25#ofbモードoutput-feedback-mode

Discussion