KLFileによるファイル入出力と整合ハッシュ/簡易暗号化について

KLib

KLibについては、まずこちらの記事を参照ください。


fopenが縛られるファイル管理

iOSのファイルを読み込む際、
バンドル(アプリに予め入れておくリソース)に含まれるファイルは
fopenで開くことができます。

しかし、よくファイル保存先に使用される
ドキュメントフォルダやテンポラリフォルダなどへの
ファイルアクセスはiOSAPIObjective-C)経由でしか正しく処理されません。
これらの場所へfopenでアクセスすると正しく動作しません。

そこで、今回はKLibに用意された
ドキュメントフォルダ内のファイルを処理する関数を
いくつかピックアップしたいと思います。


空のファイルをドキュメントフォルダに作成する

KLFile_MakeDocuments("test.txt");

ドキュメントフォルダにtest.txtという空ファイルが作成されます。
既存のファイルの場合は空ファイルで上書きます。


ドキュメントフォルダにあるファイルを読み込む

size_t filesize = 0;
bin8* pBin = KLFile_ReadDocuments("test.txt", &filesize);

test.txtを読み込み、バイナリ配列(char*と同じ)を返します。

読み込んだファイルサイズも知りたい場合は、
第二パラメータにsize_t型のポインタを渡すと
そこに読み込んだファイルサイズが入ってきます。
ファイルサイズを知るのが不要ならNULLを指定してください。


ドキュメントフォルダにあるファイルを読み込む+ファイル更新日を知る

char str[32];
bin8 pBin = KLFile_ReadDocumentsModDate( "test.txt", &filesize, str );

基本的には、KLFile_ReadDocumentsと同じですが
第三パラメータにchar配列のポインタを渡すことで、
ファイルの作成日or修正日の日付を入れて返してもらうことができます。
不要ならばNULLで構いません。

ファイル更新日の文字列は
"2012-01-23 04:56:00 +0000"のような形で返されます。


ドキュメントフォルダにある整合ハッシュ付きファイルを読み込む

// RPGのセーブデータっぽい例
typedef struct{
    u16 lv, hp, mp;
    u16 attack, guard, speed, magic; 
    u32 equip;
}SaveData;

SaveData save;
size_t filesize = 0;

if( KLFile_ReadDocumentsWithHash("rpgsave.bin", &filesize, &save) )
{
    // 整合できてファイルも読み込めた時の処理
}
else
{

     // 不整合やファイル読み込みできなかった時
     KLAlert_Open( "", "おきのどくですが、ぼうけんのしょは消えてしまいました。", "OK",NULL );
     // ついでにアラート出しの説明
     // KLAlert_Open( タイトル文字列, 文言, ボタン文字列, ボタン押しコールバック先 )

}

整合ハッシュについて後述しますが、
あらかじめWrite時もWithHashで書き出したファイルである必要があります。

WithHashの時は戻り値でバイナリがかえるのではなく、
あらかじめ決まった型の構造体などを直接渡し、そこに流しこむ形式を取っています。
ハッシュは除去された状態でかえるので、
コール側でハッシュの後始末を気にする必要はありません。

ハッシュ不整合、ファイル読込できないなど処理が正常に終了しない場合、
FALSEがかえるので、if文で分岐を行えます。


ドキュメントフォルダにあるファイルの削除

KLFile_DeleteDocuments("test.txt");

test.txtをドキュメントフォルダから削除します。
なければ処理されません。


指定パスをCFURLRef形式に変換

KLFile_CharToURLRef("YourPath");

Objective-CなどAppleAPIにはcharではなく
CFURLRefを渡せというものが存在します。
charで指定したいのに!という時はこれで変換したものを渡してください。


ドキュメントフォルダにファイルを書き込む

KLFile_WriteDocuments( "test.txt", pYourWriteValue, writeByteCount, adjustSize );

第一パラメータ…書き込むファイル名
第二パラメータ…書き込む変数のポインタ
第三パラメータ…何バイト書き込むかのサイズ
第四パラメータ…通常は0でOK
        非0でそのサイズまでファイルを切り詰めたり、
        ファイルサイズが足りなければ0埋めでそのサイズまで拡張する


ドキュメントフォルダにファイルを追記で書き込む

KLFile_WriteAddDocuments( "test.txt", pYourWriteValue, writeByteCount );

第一パラメータ…書き込むファイル名
第二パラメータ…書き込む変数のポインタ
第三パラメータ…何バイト書き込むかのサイズ

ファイルが存在すれば、その末尾に追記します。
存在しなければ作成して書き込みます。


ドキュメントフォルダに整合ハッシュ付きファイルを書き込む

KLFile_WriteDocumentsWithHash( "test.txt", pYourWriteValue, writeByteCount );

第一パラメータ…書き込むファイル名
第二パラメータ…書き込む変数のポインタ
第三パラメータ…何バイト書き込むかのサイズ

整合ハッシュについては後述しますが、
ファイルの改竄を検知するため、
簡易的なファイル整合ハッシュを頭に付与して
ファイルを保存します。

ハッシュ付きで保存したファイルはRead時もWithHashで読み込まないと
サイズが合わずにおかしくなるので注意してください。


バイナリを簡易暗号化/復元する

int test[] = { 1, 10, 100, 1000 };

// 暗号化前の数値を出力してみる
KLLog("Test:%u %u %u %u\n", test[0],test[1],test[2],test[3],);

// 簡易暗号化する( 後ろ4つは暗号化に使う適当な乱数シード )
KLMath_Encrypt( test, sizeof(int)*4, 1234, 5678, 9101, 11213 );

// 処理されたか出力してみる
KLLog("Test:%u %u %u %u\n", test[0],test[1],test[2],test[3],);

// 復元する( 後ろ4つは暗号化に使ったのと同じ乱数シード )
KLMath_Decrypt( test, sizeof(int)*4, 1234, 5678, 9101, 11213 );

// 復元されたか出力してみる
KLLog("Test:%u %u %u %u\n", test[0],test[1],test[2],test[3],);

WriteWithHashする前のバイナリをこれで簡易暗号化すると
セーブデータの解析がプレーンファイルよりは難しくできるでしょう。
使用する際は、必ず後述の注意を御覧ください。

なお、アプリ提出時の暗号化技術の使用云々については
この程度の処理は暗号化と言えるレベルでもないのでNOで問題無いはずです。
が、問題ありそうでしたらご一報頂けると助かります。


整合ハッシュについて

ファイルのバイナリから一定の法則で計算した4byteの数値で、
保存時に付与することで読み込み時に書き込んだ時と同じファイルかを整合できます。
法則を割り出されてファイルハッシュごと変えられたら元も子もないのですが、
プレーンファイルをまま保存するよりはマシ程度で使って頂けるかと思います。

セーブファイルに限らず、
ハッシュは変数のアドレスと何バイト計算するかを指定すれば
いつでも取得する事ができます。

#include "KLMath.h"

int test[10] = { 0,1,2,3,4,5,6,7,8,9 };
u32 hash = KLMath_GetHash32( test, sizeof(int)*10 );

使用上の注意

ハッシュやWithHashの関数、簡易暗号化/復元を使う場合、
アプリ毎にハッシュキー、またはランダムシードを変更してください。
これは、ハッシュ付与や暗号/復元時に内部でキーを元に乱数を作成しているためです。
キーは「KLMath.c」にKLMATH_HASHKEY_X〜Wの4つが定義されています。
乱数シードも変えたい場合は、「KLConfig.h」に4つ定義されていますので
そちらを変更してください。
(キーもConfigにまとめないとややこしいですねこれ…。)

アプリを一度リリースしたら、キーやシードは変更しない事をオススメします。
途中で変更してしまうとハッシュ計算の法則も変わり、
同じファイルでも違うハッシュになるためです。
そのため、リリース後にシードを変更してアップデートを行うと
アップデート前後でセーブデータの整合が取れなくなる事が予想されます。