先週別件でメモリ内容のハッシュをハードウエアで計算しつづけるDMACを使ってみました。ハッシュ値を求めることにも使えますが、改竄対策?かな。とりあえずのサンプルデータで動作検証したのですが、テスト用の入力パターンの生成が手作業なのは何とも。そこでラズパイ4上でテストパターン生成プログラムを試作してみました。
※ソフトな忘却力 投稿順 index はこちら
別件のDMACは、MicroChip社のArm Cortex-M4搭載マイコンATSAMD51のICM(Integrity Check Monitor)というものです。デスクリプタ(リンク可能)に記載したメモリブロック群(アドレス飛び飛びの配置でよい)を自動的にスキャンしてハッシュ値(SHA1/SHA224/SHA256)を計算しつづけ、なにか変化があったら報告してくれる優れモノです。CPUとは独立動作のDMACなので一度ソフトで設定すれば後は勝手に動きつづけます(そしてDMAC自身の設定は保護されます。)かなり複雑な構造のメモリを監視対象にできると思われます。詳しくは別件記事へ。
とは言え、ブロックに対するパディングを流石にDMACはやってくれません。動作確認用のテストパターンを入力するときは、パディング付きのメモリイメージを生成してハッシュを計算させ、期待のハッシュ値と照合して動作を確認する必要があります。
期待値の生成は簡単にできます。先週はWindows上のソフトを使いましたが、Raspberry Pi OS上であれば、sha1sumコマンドなども使用できます。しかし、チョイと困ったのが、入力データ用にパディング済の入力パターンを表示してくれるようなソフトが見つからなかったことです。そんな中間データは普通必要ないのでおよびでないっと。
まあ、どこにでもありそうなSHA1だけれど、そういう中途半端な物は無いみたいということで作ってみました。
ラズパイOSで使えるSHA1のライブラリ
SHAのヘッダは以下にありました(OSはRaspberry Pi OS buster <32bit>)。
/usr/include/openssl/sha.h
使用できるアルゴリズムは、SHA1/224/256/384/512 でした。実行オブジェクトを作る際はライブラリをリンクする必要があります。リンカへの指定は以下です。
-lcrypto
これらを踏まえて、今回使用の CMakeLists.txt は以下のようになりました(VSCodeのCMake Toolsが自動生成してくれるCMakeLists.txtに、ライブラリの指定などを手動で追加したもの。)
cmake_minimum_required(VERSION 3.0.0) project(sha1testPat VERSION 0.1.0 LANGUAGES C) set(CMAKE_C_FLAGS "-lcrypto") set(CMAKE_C_FLAGS_DEBUG "-Wall -O0 -g") set(CMAKE_C_FLAGS_RELEASE "-Wall -O3") include(CTest) enable_testing() add_executable(sha1testPat main.c) set(CPACK_PROJECT_NAME ${PROJECT_NAME}) set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) include(CPack)
作製したCソース
SHA1の計算は、
-
- SHA1_Init()で初期化して
- 末尾前のブロック数に応じた回数の SHA1_Update() で計算し、
- 最後のブロックを SHA1_Final() で〆る
という3ステップでした。使用方法は簡単なのですが、調べてみると欲しかったパディングされたメモリブロックのイメージというのは簡単に取り出せる場所には存在しないようでした。SHA1_Final()の中で、残りのデータ、パディング、そしてビット長の末尾レコードという具合にチビチビ付け加えて計算している感じです。
しかたが無いので、自前でダミーでパディング済データを作ってダンプして入力パターンとし、それとは別にSHA1_Final()でハッシュを計算してもらう、という間抜けな方式となりました。まあ一応テストパターンと照合したので計算はあっている筈。かなりカッコ悪いです。
// Generate SHA1 test pattern. LEN < 56 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/sha.h> #define MAX_TEST_PATTERN (56) void dumpHx(SHA_CTX* ptr) { printf("h0: 0x%08x\n", ptr->h0); printf("h1: 0x%08x\n", ptr->h1); printf("h2: 0x%08x\n", ptr->h2); printf("h3: 0x%08x\n", ptr->h3); printf("h4: 0x%08x\n", ptr->h4); } void dumpMEM(unsigned int* ptr) { for (int i=0; i < 16; i++) { printf("MEM[%02d]: 0x%08x\n", i, *ptr++); } } void dumpMD(unsigned int* ptr) { for (int i=0; i < 5; i++) { printf("MD[%02d]: 0x%08x\n", i, *ptr++); } } int generateTestPattern(char *dat) { SHA_CTX ctx; unsigned char buf[64]; unsigned char padding[64] = {0x80, }; unsigned char bits[8] = { 0, }; unsigned char md[SHA_DIGEST_LENGTH]; size_t slen = strlen(dat); printf("BIT LENGTH: 0x%03x (%d bytes)\n", slen * 8, slen); if (slen > MAX_TEST_PATTERN) { return -1; // ERROR } unsigned int bitLen = slen * 8; bits[7] = (bitLen & 0xFF); bits[6] = (bitLen & 0x300) >> 8; SHA1_Init(&ctx); dumpHx(&ctx); // Dummy Padding for test pattern memcpy(buf, dat, slen); memcpy(buf+slen, padding, MAX_TEST_PATTERN - slen); memcpy(buf+MAX_TEST_PATTERN, bits, 8); dumpMEM((unsigned int*)buf); // Calculate SHA1 SHA1_Update(&ctx, buf, slen); SHA1_Final(md, &ctx); //Real padding data in this function! dumpMD((unsigned int*)md); return 0; } int main(int argc, char **argv) { char testPat[] = "abc"; generateTestPattern(testPat); return 0; }
実行結果
ラズパイ4上で実行した結果が以下に。
$ ./sha1testPat BIT LENGTH: 0x018 (3 bytes) h0: 0x67452301 h1: 0xefcdab89 h2: 0x98badcfe h3: 0x10325476 h4: 0xc3d2e1f0 MEM[00]: 0x80636261 MEM[01]: 0x00000000 MEM[02]: 0x00000000 MEM[03]: 0x00000000 MEM[04]: 0x00000000 MEM[05]: 0x00000000 MEM[06]: 0x00000000 MEM[07]: 0x00000000 MEM[08]: 0x00000000 MEM[09]: 0x00000000 MEM[10]: 0x00000000 MEM[11]: 0x00000000 MEM[12]: 0x00000000 MEM[13]: 0x00000000 MEM[14]: 0x00000000 MEM[15]: 0x18000000 MD[00]: 0x363e99a9 MD[01]: 0x6a810647 MD[02]: 0x71253eba MD[03]: 0x6cc25078 MD[04]: 0x9dd8d09c
計算はできているようです。
ATSAMD51のICMは、各種SHAの規格にのっとった初期ハッシュ値を内蔵しているだけでなく、そこをユーザー独自のものに変更できる機能もあります。その場合、SHA1_Init()でセットされる値を使わず、別にSHA_CTXを「デッチあげて」食わせれば、その場合のテストもできそうです(やってないケド。)
まあ以下同文で、アルゴリズムやブロック長などを変更した場合のパターンも作れそうなので(本当か)、本件はとりあえずここまで(何時ものパターンやな。)