🔐
【Go】Goで学ぶ暗号技術 ~CTRモード~
はじめに
最近暗号技術について興味があり、少しずつ学習をしています。
第1回はECBモード、第2回はCBCモード、第3回はCFBモード、第4回はOFBモードを取り上げました。
今回はCTRモードを紹介します。
第1回 第2回 第3回 第4回
✅この記事でわかること
- CTRモードの特徴
- CTRモードの仕組み(暗号化・復号化の流れ)
- CTRモードを使う上での注意点
📝CTRモードの特徴
- Counterの略
- 「Nonce + Counter」を暗号化する
- Nonce = Number used onceの略
- IV(初期化ベクトル)同様、ランダムな値のことを指す
- Counterがインクリメントされるのを利用して暗号化
📊CTRモード図解
暗号化
- 暗号化した「Nonce + Counter」と平文をXOR
- Counterをインクリメントし、その値を暗号化し平文とXOR
復号化
- 暗号化した「Nonce + Counter」と暗号ブロックをXOR
- Counterをインクリメントし、その値を暗号化し暗号ブロックとXOR
⚠️CTRモードの注意点
- Nonceの再利用はNG
→crypto/randなどで毎回異なるNonceを生成 - Nonce + Counterの合計が16バイトになるようにする
- 認証(改ざん検出)機能がない
→認証付き暗号(AEAD)の利用も検討する
🛠Goでの実装方法
暗号化の手順
- 「Nonce + Counter」を生成
-
NewCTRで暗号化ストリームを作成 -
XORKeyStreamで平文を暗号化
復号化の手順
-
NewCTRで復号ストリームを作成 -
XORKeyStreamで復号化
実装
main.go
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"log"
)
// CTR暗号化
func encrypTCTR(key, plainText []byte) ([]byte, []byte, error) {
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
return nil, nil, fmt.Errorf("invalid key length: must be 16, 24, or 32 bytes, got %d bytes", len(key))
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, err
}
nonce := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, nil, err
}
encryptedText := make([]byte, len(plainText))
stream := cipher.NewCTR(block, nonce)
stream.XORKeyStream(encryptedText, plainText)
return encryptedText, nonce, nil
}
// CTR復号化
func decryptedCTR(key, nonce, encryptedText []byte) ([]byte, error) {
if len(key) != 16 && len(key) != 24 && len(key) != 32 {
return nil, fmt.Errorf("invalid key length: want %d bytes, got %d bytes", aes.BlockSize, len(key))
}
if len(nonce) != aes.BlockSize {
return nil, fmt.Errorf("invalid nonce length: want %d bytes, got %d bytes", aes.BlockSize, len(nonce))
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
decryptedText := make([]byte, len(encryptedText))
stream := cipher.NewCTR(block, nonce)
stream.XORKeyStream(decryptedText, encryptedText)
return decryptedText, nil
}
func main() {
key := []byte("ctr-mode-example")
plainText := []byte("Hello, CTR mode!")
fmt.Println("PlainText:", string(plainText))
encryptedText, nonce, err := encrypTCTR(key, plainText)
if err != nil {
log.Fatal(err)
}
fmt.Println("EncryptedText:", hex.EncodeToString(encryptedText))
fmt.Println("Nonce", hex.EncodeToString(nonce))
decryptedText, err := decryptedCTR(key, nonce, encryptedText)
if err != nil {
log.Fatal(err)
}
fmt.Println("DecryptedText:", string(decryptedText))
}
出力結果例
ターミナル
PlainText: Hello, CTR mode!
EncryptedText: e52a1d0056cb4c1526c5d686f35c4342
Nonce 3fc3d1ae7794196d6b11395c014139b7
DecryptedText: Hello, CTR mode!
まとめ
今回はCTRモードについて書いていきました。
IVに似ている「Nonce + Counter」というものを利用することを知りました。
次回は、GCMモードについて書いていこうと思います。
参考
Discussion