読者です 読者をやめる 読者になる 読者になる

tocsatoの備忘録

ほぼほぼ 50 代のプログラマの備忘録。swift golang javascript css html5 nginx mysql などを最近使ってます。

golang 暗号化モード

golang

crypto/cipher パッケージには複数の暗号利用モードが実装されている

crypto/cipher パッケージにはいくつかの NewXXX() が定義されています。
これら NewXXX() は使用する暗号利用モードを指しています。
なんとなく暗号化したいだけなら、暗号利用モード?という状態でも(良くも悪くも)ヘルプのサンプルコードを元に作れてしまいます。
詳細は wikipedia:暗号利用モード を参照して頂いて、ここでは暗号利用モードと NewXXX() の対応を記します。

Block

wikipedia:ブロック暗号 を表したインターフェイスです。
1ブロックの暗号化・復号を行います。

作成には、標準パッケージでは

  • crypto/aes パッケージの NewCipher(): AES-128, AES-192, or AES-256
  • crypto/des パッケージの NewCipher(): DES
  • crypto/des パッケージの NewTripleDESCipher(): 3DES, Triple DES
  • crypto/rc4 パッケージの NewCipher(): RC4, ARCFOUR

が利用できます。

CBC

Cipher Block Chainingを扱うインターフェイスを返します。

暗号化: NewCBCEncrypter()
復号: NewCBCDecrypter()

CFB

Cipher Feedbackを扱うインターフェイスを返します。

暗号化: NewCFBEncrypter()
復号: NewCFBDecrypter()

CTR

Counterを扱うインターフェイスを返します。

暗号化: NewCTR()
復号: NewCTR()

OFB

Output Feedbackを扱うインターフェイスを返します。

暗号化: NewOFB()
復号: NewOFB()

各暗号利用モードの特性

次の特性を調べてみました。

暗号変化
平文、初期化ベクトル(iv)が1ビット変化すると、暗号文すべてが変化するとき ○, 変化しないとき ✕
暗号強度
1ビット違う初期化ベクトルを与えた時、2ブロック目以降は復号できないとき ○, 復号できるとき ✕
可変長
平文の長さが可変長のとき ○, ブロックサイズの整数倍にする必要がある ✕
暗号利用モード 暗号変化 暗号強度 可変長
Block -*1
CBC
CFB
CTR
OFB

特性調査のソース

import (
	"testing"
	"crypto/aes"
	"crypto/cipher"
)

var block, _ = aes.NewCipher([]byte{
	// 適当なバイト列
	0xbb, 0xfa, 0x6e, 0x8f, 0x3b, 0x42, 0x10, 0x7c,
	0xb1, 0xdf, 0x60, 0x27, 0x0f, 0x59, 0xa0, 0x4e,
	0x7e, 0x82, 0x81, 0xc4, 0xaf, 0xb9, 0xba, 0xad,
	0xe0, 0xba, 0x22, 0xee, 0xce, 0xa3, 0x86, 0x53,
})

var (
	iv []byte
	fake []byte
	src []byte
)

func init() {
	iv = make([]byte, aes.BlockSize)
	fake = make([]byte, aes.BlockSize)
	fake[0] = 0x01
	src = make([]byte, 256)
}

func putBytes(t *testing.T, b []byte) {
	for len(b) > 0 {
		l := len(b)
		if l > 16 {
			l = 16
		}
		t.Logf("% 02x", b[:l])
		b = b[l:]
	}
}

func TestCBC(t *testing.T) {
	enc := make([]byte, 256)
	dec := make([]byte, 256)

	bm := cipher.NewCBCEncrypter(block, iv)
	bm.CryptBlocks(enc, src)
	t.Log("\nEncrypter:")
	putBytes(t, enc)

	bm = cipher.NewCBCEncrypter(block, fake)
	bm.CryptBlocks(dec, src)
	t.Log("\nFake Encrypter:")
	putBytes(t, dec)

	bm = cipher.NewCBCDecrypter(block, fake)
	bm.CryptBlocks(dec, enc)
	t.Log("\nFake Decrypter:")
	putBytes(t, dec)
}

func TestCFB(t *testing.T) {
	enc := make([]byte, 256)
	dec := make([]byte, 256)

	stream := cipher.NewCFBEncrypter(block, iv)
	stream.XORKeyStream(enc, src)
	t.Log("\nEncrypter:")
	putBytes(t, enc)

	stream = cipher.NewCFBEncrypter(block, fake)
	stream.XORKeyStream(dec, src)
	t.Log("\nFake Encrypter:")
	putBytes(t, dec)

	stream = cipher.NewCFBDecrypter(block, fake)
	stream.XORKeyStream(dec, enc)
	t.Log("\nFake Decrypter:")
	putBytes(t, dec)
}

func TestCTR(t *testing.T) {
	enc := make([]byte, 256)
	dec := make([]byte, 256)

	stream := cipher.NewCTR(block, iv)
	stream.XORKeyStream(enc, src)
	t.Log("\nEncrypter:")
	putBytes(t, enc)

	stream = cipher.NewCTR(block, fake)
	stream.XORKeyStream(dec, src)
	t.Log("\nFake Encrypter:")
	putBytes(t, dec)

	stream = cipher.NewCTR(block, fake)
	stream.XORKeyStream(dec, enc)
	t.Log("\nFake Decrypter:")
	putBytes(t, dec)
}

func TestOFB(t *testing.T) {
	enc := make([]byte, 256)
	dec := make([]byte, 256)

	stream := cipher.NewOFB(block, iv)
	stream.XORKeyStream(enc, src)
	t.Log("\nEncrypter:")
	putBytes(t, enc)

	stream = cipher.NewOFB(block, fake)
	stream.XORKeyStream(dec, src)
	t.Log("\nFake Encrypter:")
	putBytes(t, dec)

	stream = cipher.NewOFB(block, fake)
	stream.XORKeyStream(dec, enc)
	t.Log("\nFake Decrypter:")
	putBytes(t, dec)
}

*1:初期化ベクトル(iv)の指定がない