俺の AES CFB が復号処理で復号できるわけがない

ESP-IDF に転生した mbedtls は AES CFB を復号 API で復号できる異能力者だった!?

ファームウェアエンジニアの中林 (id:tomo-wait-for-it-yuki) です。今回は出落ちです。 みなさん暗号化は正しくお使いでしょうか? 本エントリでは最近発覚したちょっとしたトラブルをご紹介します。

タイトル補足。復号処理で復号できるは当然なのですが、mbedtls 的には AES 暗号化と復号化とが別 API として提供されています。

背景

Nature Remo ではアプリとの通信で共通鍵暗号方式の AES CFB を使用するコードがあります。 このコードはプロダクトローンチ後からずっと正常に機能を果たしてきたものです。

暗号ライブラリには mbedtls を使っており、こちらは Linux へもポーティングされています。 そこで、最近、Remo のコードを Linux 上で動かして機能レベルのテストや動作確認ができるように移植作業をしていました。 その矢先です…

発生した問題

移植作業を経て、ほとんどのコードが Linux 上で正常に動作するようになりました。 しかし、なぜか、どうしても、AES CFB で受信したデータを復号すると、Linux 上でデータが化けてしまうことに気が付きました。

調査開始

どうしても Linux で動かないので、俺はまためんどうなことになったなぁ、とか
そういや昼飯も食っていないなぁとか色々な思いを巡らせつつも調査を開始することにしたのである1

まずは自分の移植した部分やバッファオーバーランを疑って調査しましたが、どうもそうではないことがわかってきました。 共通鍵暗号の復号化まわりに原因が絞れてきたので、後は

  1. DH 鍵交換
  2. AES CFB 復号処理

のどちらかがおかしい、という目処がついてきます。

このあたりで、モバイルエンジニアの亀田さんにも手伝ってもらって、アプリ側の暗号鍵を固定する特別版アプリを作ってもらい、DH 鍵交換が正常に動作していることを確認しました。

となると最後は AES CFB 復号処理です。

ファームウェアの復号処理は、次のようになっていました。

    int r;
    mbedtls_aes_context ctx;
    mbedtls_aes_init(&ctx);

    uint8_t psk[16];
    mbedtls_md5(secret_key, 128, psk);
    r = mbedtls_aes_setkey_dec(&ctx, psk, 128);

    size_t iv_offset = 0;
    r = mbedtls_aes_crypt_cfb128(
        &ctx,
        MBEDTLS_AES_DECRYPT,
        data_len,
        &iv_offset,
        iv,
        in_out_crypto_data,
        in_out_crypto_data);

一見何も問題なさそうに見えます。 しかし、アプリ側で固定してもらった暗号鍵とアプリから送信されてきた暗号データを使った最小限のテストを作ってみたところ、データの復号に失敗しました

ということは、これは mbedtls の使い方間違っていそうだな、ということで改めて AES CFB のアルゴリズム を確認していて、そう言えば暗号化と復号化の処理が同じ暗号方式だった、ということを思い出します。

さらに mbedtls の API リファレンスを見てみると…

os.mbed.com

Note: Due to the nature of CFB you should use the same key schedule for both encryption and decryption. So a context initialized with mbedtls_aes_setkey_enc() for both MBEDTLS_AES_ENCRYPT and MBEDTLS_AES_DECRYPT.

はい、AES CFB では暗号化も復号化も同じ処理なので、暗号化でも復号化でも同じ API mbedtls_aes_setkey_enc() を使わないとダメですよ、とあります。 たしかにファームウェアのコードでは、mbedtls_aes_setkey_dec() を使っていました。

ここを修正したところ、無事、Linux 上でも AES CFB の復号処理が正常に動作するようになりました。

じゃあなんで Remo では復号できるんだよ!?

となると不可解なのは、どうして Remo では復号できてしまっているのでしょうか? その謎を探るべく、ESP-IDF の奥地へと

mbedtls_aes_setkey_enc / mbedtls_aes_setkey_dec がマクロで同じ esp_aes_setkey 呼び出しに…

「ドカーン!私はふりかえった。死んだ」

おまけ〜タイトルボツ案たち〜

余談ですが、この問題自体は1ヶ月以上前に解決していて、せっかくハマったのだからブログネタくらいにはしないと割が合わない、と考えていたのですが、良いタイトルが思いつかずお蔵入りになるところでした。

そんなときに twitter で見かけたのが次の記事です。

zenn.dev

なるほどライトノベル風。

1

AES CFB 暗号化「ひょっとして」
AES CFB 復号化「わたしたち」
「「入れ替わってるー!?」」

本文中で使えるネタに乏しいので仕方ない。

2

ちょうど、twitter で ChatGPT にライトノベル風のタイトルを作らせるとおもしろい、というのが流行っていたのでやってみました。

chatgpt-1

復号したら中身が読めて欲しいし、そんな噂はない。

chatgpt-2

暗号技術の闇…

chatgpt-3

マジもマジ。むしろバレない方がおかしい。

chatgpt-4

そのとおりだけど!めっちゃグサッとくるやん!

chatgpt-5

現実はきびしい…

エンジニア積極採用中です

Natureでは恥ずかしい物語でもブログネタにできる仲間を募集しています。

herp.careers

カジュアル面談も歓迎なので、ぜひお申し込みください。

herp.careers

Natureのミッション、サービス、組織や文化、福利厚生についてご興味のある方は、ぜひCulture Deckをご覧ください。

speakerdeck.com