不定期に寝て7時半に起きた。起きてからシャワー浴びたらしゃんとした。昨日と同様に昼間働いて、ホテルに戻ってきて2-3時間寝てから夜に2-3時間かけて次のコードを書いた。
go での暗号化/復号化
ある機密情報を扱うので暗号化して普通には「みえない」ようにしたい。まったくわからないのでググったら次の記事がみつかった。
まずはこの記事にあるサンプルコードを動かしてみながら内容を理解する。暗号技術そのものは理解できないが、コードの振る舞いそのものは動かしながら理解できる。その後、いろいろ調べているとこのサンプルコードの大半は crypto/cipher パッケージにあるサンプルコードと同じであることに気づく。標準ライブラリのドキュメントでは iv (initialization vector) を暗号化対象の文字列の一部を切り取って生成している。iv は一意である必要はあるが、セキュアでなくてもよいとある。よくあるやり方とあるのでサンプルコードをみながら同じように実装した。
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
最終的な暗号化と復号化のコードは次になった。標準ライブラリにあるサンプルコードと基本的には同じ。
func Encrypt(secret string, plainText []byte) ([]byte, error) {
block, err := aes.NewCipher([]byte(secret))
if err != nil {
return nil, fmt.Errorf("failed to create chiper: %w", err)
}
cipherText := make([]byte, aes.BlockSize+len(plainText))
iv := cipherText[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, fmt.Errorf("failed to read for iv: %w", err)
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(cipherText[aes.BlockSize:], plainText)
buf := make([]byte, base64.StdEncoding.EncodedLen(len(cipherText)))
base64.StdEncoding.Encode(buf, cipherText)
return buf, nil
}
func Decrypt(secret string, text []byte) ([]byte, error) {
block, err := aes.NewCipher([]byte(secret))
if err != nil {
return nil, fmt.Errorf("failed to create chiper: %w", err)
}
dbuf := make([]byte, len(text))
n, err := base64.StdEncoding.Decode(dbuf, text)
if err != nil {
return nil, fmt.Errorf("failed to create chiper: %w", err)
}
cipherText := dbuf[:n]
if len(cipherText) < aes.BlockSize {
return nil, fmt.Errorf("ciphertext is too short: %d", len(cipherText))
}
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(cipherText, cipherText)
return cipherText, nil
}