ソフトな忘却力(12) RPi3、AES、1ブロックだけのテストパターン「素の」暗号化

Joseph Halfmoon

前回は、ATSAMD51マイコンのSHA1計算ハードを試用するプログラムを書いた後で、テストパターン生成用のプログラムを作りました。こういう泥縄ではいかん、ということで、今回はマイコンプログラムの前にテストパターン生成用のプログラムを作っておくことにいたしました。AES暗号化、1ブロック「だけ」です。

前回のSHA1にせよ、今回のAESにせよ、ホストマシン上での実用のためには立派なプログラムがいろいろあるので、敢えて自分で作ろうなどとは思いませぬ。しかし、ハードのテスト(動作確認)用(もしかするとデバッグにも使う)としては、「途中経過」など欲しいです。最後の結果をポンと出されて相違があっても、どこでどう設定を間違ったんだかよく分からない。

AES忘れていたので復習

AES、遥かな昔に一度勉強した記憶があるのですが、完全に忘れていました。いろいろ調べながらポイントをまとめてみるとアイキャッチ画像のようになりました。本当に良いのか?これで。

  1. AESは1ブロック128ビット毎に暗号化する
  2. 暗号化された出力も1ブロック128ビット
  3. 暗号化には共通鍵128/192/256ビットを使う
  4. 共通鍵のビット長に応じて暗号化の「ラウンド数」が異なる
  5. 共通鍵から各ラウンドで使うラウンド鍵が生成される

最小単位の1ブロックに限れば外部とのインタフェースは上記のように、入力、出力そして共通鍵という3要素で規定できるのでシンプル。これからテストしようとするハードウエアもまずはこれが基本。

複数ブロックにまたがる現実の暗号化の場合、「暗号利用モード」とうものがあり、ECB,CBC,CFB,OFB,CTRなどの「上位層」の処理があり、モードによってイニシャルベクタIVとか、役者が増えていきます。マイコンハードの方も各種モードをサポートしており、例によってDMAでブロックをかき集めてきたりもできます。

しかし使ってみる初回でもあり今回は、マイコン搭載のAES計算ハードウエアに、素性の知れた入力パターンと共通鍵を入れたら、期待通りの暗号が出てくるよね、という確認のみといたしました。毎度深みにハマるDMACなどには登場願わないつもりです。そこで普通に長いデータ相手には「よゐこは使わないでね」的なプリミティブなモード ECB(Electronic Code Book)を適用。そしてAESの鍵も一番短い128ビットといたしました。「素」暗号というべきか。

まずはこれでハードを動かしたい。

ラズパイ上のツールを試用

前回はラズパイ4機でしたが、今回は諸般の事情ありラズパイ3機で作業しています。とはいえ同じOS(Raspberry Pi OS buster <32bit>)で動作しているので特に変わりはないです。

ラズパイ上のコマンドライン・ツールとしては、以下にオフィシャルサイトへのURLを貼り付けましたが、openssl を使うのが定番のようでした。

openssl-enc

今回想定しているのは、以下のとおりです。

  • 入力パターンは16バイト(128ビット)
  • 共通鍵も16バイト(128ビット)AES128。
  • ECBモード(ブロック間の関係なし)

これで素の暗号化をして16バイトの結果が欲しいです。ちなみに、入力パターンとユーザ鍵は以下のとおりの16バイト文字列です。ファイル上きっかり16バイトで後ろにLFとか付いてないことを確認してあります。

in0.txt:
0123456789abcdef

pass.txt:
ABCDEFGHIJKLMNOP

実際、コマンドラインからopenssl を使ってエンコードしてみたものがこちら。エンコード出力を16進ダンプして中身を覗いています。

$ openssl enc -e -in in0.txt -out outSALT.txt -aes-128-ecb -pass pass:pass.txt
$ xxd outSALT.txt
00000000: 5361 6c74 6564 5f5f 532d 27d1 dc13 795d Salted__S-'...y]
00000010: a5c5 bb6c 50c8 0a93 565f 41c4 0574 c569 ...lP...V_A..t.i
00000020: d3da 6043 9d8b 19b5 e092 59b4 ffb9 793c ..`C......Y...y<

なんだかな~、先頭には Salted__などというお印がついており、「塩ふって」あるみたいです。明らかに当方期待の「素」の暗号化以上のことをやってくれちゃっているみたい。ショッパイ。長さも3ブロック分の48バイトもあるし。。。

調べると、-nosalt というオプションがあるようだったので、そちらも試してみました。こんな感じ。

$ openssl enc -e -in in0.txt -out outNOSALT.txt -aes-128-ecb -nosalt -pass pass:pass.txt
$ xxd outNOSALT.txt
00000000: 2faa 576c 1e0d 40e1 4ab2 1229 68bc a093 /.Wl..@.J..)h...
00000010: f908 25a7 bc50 ead5 ecbd a37c df56 7f7e ..%..P.....|.V.~

冒頭のSalted__こそ消えましたが、結果は2ブロック分、32バイトあります。暗号化ソフトとしては、当方がやりたいような「素」の暗号化は危ないのでいろいろしてくれるのでしょうがねえ。上記では省略してますが、こんなパスワード文字列じゃ危ないからこれ使った方がいいみたいな警告がいろいろ出てます。

なお、以下のようにすれば元の文字列に復号できます。-nosaltで暗号化したものは、-nosaltで復号しないとならないようです。

$ openssl enc -d -in outSALT.txt -aes-128-ecb -pass pass:pass.txt
$ openssl enc -d -in outNOSALT.txt -aes-128-ecb -nosalt -pass pass:pass.txt
テストパターン生成プログラムを試作

前回、openssl配下のsha.hを使ったときに、aes.hもそこにあることを確かめてあったので、今回は迷うことなく、それを参照してラズパイ上のプログラムを書いてみます。ただ、APIドキュメント的なものが見当たらなかったので、ヘッダファイルから勝手に推測して使ってます。もしかすると間違っているかもしれないです。

ちゃんと使うにはいろいろあるんだと思いますが、今回のように1ブロックのみECBで素の暗号化の場合には、

  1. AES_set_encrypt_key() 関数で、鍵拡張をやっておく
  2. AES_ecb_encrypt() 関数に、上記の鍵と入力(平文)を渡せば、暗号化してくれる

ということでいいんじゃないかと考えました。こんな感じ。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>

unsigned char inbuf[] = "0123456789abcdef";
unsigned char ukey[]  = "ABCDEFGHIJKLMNOP";
unsigned char outbuf[128];
AES_KEY key;

void dumpKey(AES_KEY *ky) {
    printf("Rounds: %d\n",ky->rounds);
    int kyMax = ky->rounds;
    for (int i=0; i<kyMax; i++) {
        printf("%02d:",i);
        for (int j=0; j<4; j++) {
            printf(" 0x%08x",ky->rd_key[i*4+j]);
        }
        printf("\n");
    }
}

void dumpBuf(unsigned char *buf) {
    printf("OUT:\n");
    for (int i=0; i<4; i++) {
        printf("%02d:",i);
        for (int j=0; j<4; j++) {
            printf(" 0x%02x",buf[i*4+j]);
        }
        printf("\n");
    }
}

int main(int argc, char **argv) {
    printf("AES128 1 BLOCK test program.");
    AES_set_encrypt_key(ukey, 128, &key);
    dumpKey(&key);
    AES_ecb_encrypt(inbuf, outbuf, &key, AES_ENCRYPT);
    dumpBuf(outbuf);
}

なお、例によってPC上のVSCodeからリモート接続し、CMake Tools使ってビルドしています。その時使ったCMakeLists.txtは以下です(ツールが自動生成してくれたものにオプション -lcrypto を付け加えた。)

cmake_minimum_required(VERSION 3.0.0)
project(aesTST
    VERSION 0.1.0
    LANGUAGES C)

set(CMAKE_C_FLAGS "-lcrypto")

include(CTest)
enable_testing()

add_executable(aesTST main.c)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
実行結果

ラズパイ3上でビルドした実行ファイルを走らせてみます。Round数は10と予定どおりです。そして各ラウンドのキー(128ビット)を10個ダンプしています。なんだか分からないけれど「拡張」してくれているみたい。

そして末尾に1ブロック分16バイト(128ビット)の暗号らしきものが出力されてとります。

$ ./aesTST
AES128 1 BLOCK test program.Rounds: 10
00: 0x41424344 0x45464748 0x494a4b4c 0x4d4e4f50
01: 0x6fc610a7 0x2a8057ef 0x63ca1ca3 0x2e8453f3
02: 0x322b1d96 0x18ab4a79 0x7b6156da 0x55e50529
03: 0xef40b86a 0xf7ebf213 0x8c8aa4c9 0xd96fa1e0
04: 0x4f72595f 0xb899ab4c 0x34130f85 0xed7cae65
05: 0x4f96140a 0xf70fbf46 0xc31cb0c3 0x2e601ea6
06: 0xbfe4303b 0x48eb8f7d 0x8bf73fbe 0xa5972118
07: 0x77199d3d 0x3ff21240 0xb4052dfe 0x11920ce6
08: 0xb8e713bf 0x871501ff 0x33102c01 0x228220e7
09: 0xb050872c 0x374586d3 0x0455aad2 0x26d78a35
OUT:
00: 0x39 0xb3 0x9d 0x6f
01: 0x02 0xe1 0x2d 0xc4
02: 0xec 0xea 0x17 0x84
03: 0x84 0xad 0xfc 0xc0

でもな~このプログラム自体の正しさが分からんなあ。マイコンペリフェラル回路のハード計算の結果と突き合わせるしかないなあ。本末転倒だなあ。またもや泥縄か。

ソフトな忘却力(11) RPi4、SHA1、パディング入りのテストパターンとダイジェスト へ戻る