これはひどい!

2.6.x系のLinuxでストレージの暗号化を行うには、大きく2つの方法がある。
一つはdm-cryptを使うのもので、これはFileSystemとBlockDeviceの間に仮想的なレイヤを挟み、ここをI/Oが通過する間にデータの暗号化/復号化を行うもの。ディスク全体を暗号化するが、ブロックレイヤを持たないMTDデバイス(FlashROMなど)では利用できない。

もう一つはeCryptFSというもので、これはNFSのように他のディレクトリの上に覆い被さり、これをeCryptfs経由で読み書きする事で、ファイル単位の暗号化/複合化を行う。eCryptfsはデバイスやファイルシステムを選ばずに利用できるため、現時点では組込みLinuxでストレージを暗号化する際の(標準カーネルにマージ済みの機能では)唯一の選択肢である・・・。
が、メタ情報をファイル自身が持つという性質が、トンでもない事態を引き起こしてしまうようだ。下の実行ログを見てもらいたい。

■ eCryptfs無しの場合
(※) データ長16byteのファイルを512個作成

# mount -t jffs2 /dev/mtdblock0 /mnt/0
# df
Filesystem   1K-blocks     Used Available Use% Mounted on
    :
/dev/mtdblock0    4096      388      3708  10% /mnt/0
# for x in `seq 1 512`
do
    echo "this is 16 bytes" > /mnt/0/file_$x
done
# df
Filesystem   1K-blocks     Used Available Use% Mounted on
    :
/dev/mtdblock0    4096      504      3592  13% /mnt/0
  → 増分は +116KB

一方、eCryptfsを有効にすると、以下の例では /mnt/1 への読み書きが暗号化され /mnt/0 に格納される。

■ eCryptfs有りの場合

# mount -t jffs2 /dev/mtdblock0 /mnt/0
# mount -t ecryptfs /mnt/0 /mnt/1 -o sig=aes
# for x in `seq 1 512`
do
    echo "this is 16 bytes" > /mnt/1/file_$x
done
# df
Filesystem    1K-blocks      Used Available Use% Mounted on
    :
/dev/mtdblock0     4096      2640      1456  65% /mnt/0
/mnt/0             4096      2640      1456  65% /mnt/1
  → 増分は +2.25MB!

で、この状態で /mnt/0 を見てみると、ファイルサイズが12,288 (= 3 * PAGE_SIZE) になっている。

Ext3など通常のブロックデバイス上に作られるファイルは、どんなに小さくても1KB程度のディスク上の領域を占有するし、なによりディスク単価を考えればあまり気にはならないかもしれない。

だが、JFFS2の場合、ファイルのデータ領域のサイズは4byte単位で調整されるので、これだけ無駄な領域を作られると差分が気になる。なにより、区画の大きさが32MBとか64MB程度のファイルシステムでこれはひどい!

あと余談。
JFFS2は圧縮オプションを持っていて、メディアへの書込み時にデータを圧縮して書き込むことができる。
下は非常に極端なケースで、/dev/zero (全て0) のデータをひたすら一個のファイルに書き込んだ場合。圧縮効率は最大となる。

# modprobe mtdram
# modprobe mtdblock
# mount -t jffs2 /dev/mtdblock0 /mnt/0
# df
Filesystem    1K-blocks      Used Available Use% Mounted on
    :
/dev/mtdblock0     4096       388      3708  10% /mnt/0
[root@masu ~]# dd if=/dev/zero of=/mnt/0/hoge bs=65536
dd: writing `/mnt/0/hoge': No space left on device
1765+0 records in
1764+0 records out
115609600 bytes (116 MB) copied, 2.98746 s, 38.7 MB/s

4MBの区画に116MBのデータを書き込めた。

だが、eCryptfsを使った場合、下にJFFS2があろうがExt3があろうが、データを全て暗号化して書き込む。
したがって、ビット列は完全にランダム化されており、圧縮効率は最悪となる。
手元でなんとか実証してみたかったが、でかいデータをeCryptfs経由で書き込もうとしたら lockdep がデッドロックを検出し、そもそも動かなかった。Oops。

という訳で、組込みLinux向けのディスク暗号化エンジンが必要なのです。