OpenSSL の AES の GCM モードで作った暗号を go で復号する

はじめに

クライアント側で暗号化したデータを gin で復号しようとしてちょっと詰まったのでメモを残しておきます

こちら に encription と decription のサンプルがあるのですが

	block, err := aes.NewCipher(key)
	if err != nil {
		panic(err.Error())
	}

	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		panic(err.Error())
	}

	plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		panic(err.Error())
	}

  fmt.Printf("%s\n", plaintext)

key を引数にして aes で block cipher を作って、そこから aesgcm を作って、aesgcm.Open() に nonce と暗号文を渡すと復号してくれるみたいです

nonce って要するに IV の事だとして、aad と tag はどうやって指定するんだろ? と、傍と困ってしまったわけです

aesgcm.Open()

なんか引数が4つあって、nonce と ciphertext 以外に nil を渡しているので、そこにそれぞれ aad と tag がはいるんだろうとおもってドキュメントを探します

AEAD のインターフェースが見つかりました

  Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error)

後ろが aad で前が dst、どっちもなきゃないでそういうものなので、それで nil にできたわけですね、では tag はどこに?

tag は ciphertext の後ろにつなげる

わからないので aes gcm tag とかでググりまくってて見つけたのが gcm のコードで、214行目を見た瞬間に脳天をハンマーで殴られたような衝撃を感じました

  tag := ciphertext[len(ciphertext)-g.tagSize:]
  ciphertext = ciphertext[:len(ciphertext)-g.tagSize]

なななぁんとおおおお、go の gcm は暗号文の後ろにtagがくっついているという暗黙の前提があったんですね!
って、あのなあ、このインターフェース考えた奴ちょっとこっちこいゴルァ!少なくともドキュメント梨がゆるされるものかどうか少し反省 time をとってほしい希ガス

正解

というわけで ciphertext の後ろに tag を append して渡してあげると、無事に復号してくれました

  ct := append(ciphertext, tag...)
	plaintext, err := aesgcm.Open(nil, nonce, ct, aad)

おまけ

cipher や tag を Base64 で文字列にして http POST で server に投げるときにもはまりポイントがあるのできをつけましょうというメモです

Base64URL にする

Base64 の + とか(わざわざこれを Base64のエンコーデッドなアルファベットに入れてくださった先人の頭を蹴っ飛ばしたい思いです)をリクエストの body に入れて post すると消えてしまうので、+- に、/_ に変換する Base64URL という形にします

OpenSSL の Base64 を探してみたのですが Base64URL は無さそうだったのと(Base64 そのものは OpenSSL にあります)、探す手間でできちゃいそうだったのでこんなかんじに

int plus2minus(unsigned char *buff, int buff_len){
    for (int i=0; i< buff_len; i++){
        if (buff[i] == 0){
            return 1;
        }
        if (buff[i] == '+'){
            buff[i] = '-';
        }
        if (buff[i] == '/'){
            buff[i] = '_';
        }        
    }
    return 1;
}

コードがアセアセしてる顔文字みたいでかわいいですね

base64Url の go 側のデコードは URLEncoding を使う

go 側ではモジュール encoding/base64 が URL Encoding を持ってますのでそれを使います

decoded, err := base64.URLEncoding.DecodeString(base64urlstr)

performance

gin の handler で見てると Base64url のポストを受けて復号が終わるまで Scaleway の C1 サーバ(初代の ARM BareMetal Server です。RPi よりちょっと速い感じ)でも 500μs 程度みたいです
gin 使うようになって 1ms を「遅いな」と感じるような贅沢さんになってしました ^^/


Last Updated: 7/4/2020, 2:24:03 AM