フルディスク暗号化とは、暗号化された鍵を使用して Android デバイス上のすべてのユーザーデータをエンコードするプロセスです。デバイスが暗号化されると、ユーザーが作成したすべてのデータはディスクにコミットされる前に自動的に暗号化され、すべての読み取りで呼び出しプロセスに戻す前にデータが自動的に復号されます。
フルディスク暗号化は Android 4.4 で導入されましたが、Android 5.0 では次の新機能が導入されました。
- 最初の起動に時間がかかるのを避けるために、データ パーティション上の使用済みのブロックのみを暗号化する高速な暗号化が作成されました。現在は ext4 と f2fs ファイルシステムのみが高速な暗号化をサポートしています。
- 初回起動時に暗号化を行う
forceencryptfstab フラグが追加されました。 - パターンとパスワードなしの暗号化のサポートが追加されました。
- Trusted Execution Environment(TEE)の署名機能(TrustZone 上など)を使用したハードウェア格納型の暗号鍵ストレージが追加されました。詳しくは、暗号化された鍵の格納をご覧ください。
注意: Android 5.0 にアップグレードして暗号化したデバイスは、データが初期化されて暗号化されていない状態に戻ります。最初の起動時に新しい Android 5.0 デバイスが暗号化されると、暗号化されていない状態に戻すことはできません。
Android のフルディスク暗号化の仕組み
Android のフルディスク暗号化は dm-crypt に基づいています。これは、ブロック デバイス レイヤで動作するカーネル機能です。したがって暗号化には、ブロック デバイスとしてカーネルに認識される Embedded MultiMediaCard(eMMC)などのフラッシュ デバイスが利用されます。未加工の NAND フラッシュ チップと直接通信する YAFFS では暗号化できません。
暗号化のアルゴリズムは、CBC(暗号ブロック チェーン)と ESSIV:SHA256 による 128 AES(Advanced Encryption Standard)です。マスター鍵は、OpenSSL ライブラリへの呼び出しを介して 128 ビット AES で暗号化されます。鍵には 128 ビット以上を使用する必要があります(256 は任意です)。
注: OEM は 128 ビット以上を使用してマスター鍵を暗号化できます。
Android 5.0 リリースでは、4 種類の暗号化状態があります。
- デフォルト
- PIN
- パスワード
- パターン
デバイスは最初の起動時にランダムに生成された 128 ビットのマスター鍵を作成し、デフォルトのパスワードと保存されたソルトでハッシュします。デフォルトのパスワードは「default_password」ですが、結果として生じるハッシュも、署名のハッシュを使用してマスター鍵を暗号化する TEE(TrustZone など)によって署名されます。
デフォルトのパスワードは、Android オープンソース プロジェクトの cryptfs.cpp ファイルで定義されています。
デバイスに PIN、パス、またはパスワードを設定すると、128 ビットの鍵のみが再暗号化されて保存されます(つまり、ユーザーの PIN、パス、パターンを変更してもユーザーデータは再暗号化されません)。管理対象デバイスは PIN、パターン、またはパスワードの制限を受ける場合があることに注意してください。
暗号化は init と vold によって管理されます。
init が vold を呼び出し、vold は init でイベントをトリガーするプロパティを設定します。システムの他の部分では、ステータスの報告、パスワードの要求、致命的なエラーの発生時に出荷時設定にリセットするかどうかの確認などのタスクを実行するプロパティがチェックされます。vold の暗号化機能の呼び出しには、コマンドライン ツール vdc の cryptfs コマンド(checkpw、restart、enablecrypto、changepw、cryptocomplete、verifypw、setfield、getfield、mountdefaultencrypted、getpwtype、getpw、clearpw)が使用されます。
/data を暗号化、復号、またはワイプするための /data のマウントはしないでください。ただし、ユーザー インターフェース(UI)を表示するには、フレームワークを開始して /data を実行する必要があります。この問題を解決するために、一時的なファイルシステムが /data にマウントされます。これにより、Android は必要に応じてパスワードの入力を求めたり、進行状況を表示したり、データのワイプを提案したりできます。この場合、一時ファイルシステムから実際の /data ファイルシステムに切り替えるために、一時ファイルシステム上でファイルを開いているすべてのプロセスを停止し、実際の /data ファイルシステムでそのプロセスを再開する必要が生じるという制限が課されます。このため、すべてのサービスは 3 つのグループ(core、main、late_start)のいずれかに分類されます。
core: 起動後は絶対にシャットダウンしない。main: シャットダウンし、ディスク パスワードの入力後に再起動する。late_start:/dataの復号とマウントが完了するまで起動しない。
これらのアクションをトリガーするには、vold.decrypt プロパティにさまざまな文字列を設定します。サービスを停止して再起動する init コマンドは次のとおりです。
class_reset: サービスを停止し、class_start で再起動できるようにします。class_start: サービスを再起動します。class_stop: サービスを停止してSVC_DISABLEDフラグを追加します。 停止されたサービスはclass_startに応答しません。
フロー
暗号化されたデバイスには 4 つのフローがあります。デバイスは 1 度だけ暗号化され、通常の起動フローを実行します。
- 暗号化されていないデバイスの暗号化:
forceencryptを使用して新しいデバイスを暗号化する: 初回起動時の必須の暗号化です(Android L 以降)。- 既存のデバイスを暗号化する: ユーザーによって開始される暗号化です(Android K 以前)。
- 暗号化されたデバイスの起動:
- パスワードなしで暗号化されたデバイスを起動する: パスワードが設定されていない、暗号化されたデバイスを起動します(Android 5.0 以降を搭載したデバイス)。
- パスワードで暗号化されたデバイスを起動する: パスワードが設定されている、暗号化されたデバイスを起動します。
これらのフロー以外に、デバイスが /data の暗号化に失敗する可能性もあります。
それぞれのフローについて以下で詳しく説明します。
forceencrypt を使用した新しいデバイスの暗号化
これは Android 5.0 デバイスの通常の初回起動です。
forceencryptフラグを使用して、暗号化されていないファイルシステムを検出する/dataが暗号化されていませんが、forceencryptによって強制されているため暗号化する必要があります。/dataのマウントを解除します。/dataの暗号化を開始するvold.decrypt = "trigger_encryption"によってinit.rcがトリガーされて、voldがパスワードなしで/dataを暗号化します(新しいデバイスなのでパスワードは設定されていません)。- tmpfs をマウントする
voldがro.crypto.tmpfs_optionsの tmpfs オプションを使用して tmpfs/dataをマウントし、プロパティvold.encrypt_progressを 0 に設定します。voldは暗号化されたシステムを起動するために tmpfs/dataを準備し、プロパティvold.decryptをtrigger_restart_min_frameworkに設定します。 - 進行状況を表示するフレームワークを呼び出す
実質的にデバイスには暗号化されるデータが存在せず、暗号化は短時間で実行されるため、実際に進行状況バーが表示されることはほとんどありません。進行状況の UI について詳しくは、既存のデバイスの暗号化をご覧ください。
/dataが暗号化されたら、フレームワークを停止するvoldがvold.decryptをtrigger_default_encryptionに設定し、これによりdefaultcryptoサービスが開始されます(デフォルトで暗号化されたユーザーデータをマウントするために、以下のフローが開始されます)。trigger_default_encryptionによって暗号化タイプがチェックされ、/dataの暗号化にパスワードが使用されているかどうかが確認されます。Android 5.0 デバイスは初回起動時に暗号化されるため、パスワードは設定されていません。したがって、/dataを復号してマウントします。/dataをマウントする次に
initは、init.rcに設定されているro.crypto.tmpfs_optionsから取得したパラメータを使用して、tmpfs RAMDisk に/dataをマウントします。- フレームワークを開始する
voldがvold.decryptをtrigger_restart_frameworkに設定し、通常の起動プロセスを続行します。
既存のデバイスの暗号化
これは、L に移行された暗号化されていない Android K 以前のデバイスを暗号化する場合に該当します。
ユーザーによって開始されるこのプロセスは、コードでは「インプレース暗号化」と呼ばれます。ユーザーがデバイスの暗号化を選択すると、暗号化処理を完了するのに十分な電力が確保されるように、UI は電池が完全に充電されて AC アダプターが接続されていることを確認します。
警告: 暗号化が終了する前にデバイスの電源が切れてシャットダウンすると、ファイルのデータは部分的に暗号化された状態になります。この場合はデバイスを初期状態にリセットする必要があり、データはすべて失われます。
インプレース暗号化を有効にするために、vold はループを開始して実際のブロック デバイスの各セクターを読み取り、暗号ブロック デバイスに書き込みます。vold はセクターの読み取りと書き込みを行う前に使用中であるかどうかを確認して、データがほとんどまたはまったくない新しいデバイスでの暗号化を高速化します。
デバイスの状態: ro.crypto.state = "unencrypted" を設定し、on nonencrypted init トリガーを実行して起動を続行します。
- パスワードを確認する
UI はコマンド
cryptfs enablecrypto inplace(passwdはユーザーのロック画面のパスワード)でvoldを呼び出します。 - フレームワークを停止する
voldはエラーをチェックし、暗号化できない場合は -1 を返してログに理由を出力します。暗号化できる場合は、プロパティvold.decryptをtrigger_shutdown_frameworkに設定します。これにより、init.rcはlate_startクラスとmainクラスのサービスを停止します。 - 暗号フッターを作成する
- パンくずリストファイルを作成する
- 再起動する
- パンくずリストファイルを検出する
/dataの暗号化を開始する次に
voldが暗号マッピングを設定します。これにより、実際のブロック デバイスにマッピングされる仮想暗号ブロック デバイスが作成され、各セクターを書き込みのたびに暗号化し、読み込みのたびに復号します。その後、voldは暗号メタデータを作成して書き出します。- 暗号化中に tmpfs をマウントする
voldがro.crypto.tmpfs_optionsの tmpfs オプションを使用して tmpfs/dataをマウントし、プロパティvold.encrypt_progressを 0 に設定します。voldは暗号化されたシステムを起動するために tmpfs/dataを準備し、プロパティvold.decryptをtrigger_restart_min_frameworkに設定します。 - 進行状況を表示するフレームワークを呼び出す
trigger_restart_min_frameworkによってinit.rcはmainクラスのサービスを開始します。フレームワークでvold.encrypt_progressが 0 に設定されると、進行状況バーの UI が表示され、5 秒ごとにプロパティが照会されて進行状況バーが更新されます。暗号化ループは、暗号化されたパーティションの割合が変わるたびにvold.encrypt_progressを更新します。 /dataが暗号化されたら、暗号フッターを更新する/dataが正常に暗号化されると、voldはメタデータのフラグENCRYPTION_IN_PROGRESSをクリアします。デバイスのロック解除が成功すると、パスワードを使用してマスター鍵が暗号化され、暗号フッターが更新されます。
なんらかの理由で再起動が失敗した場合、
voldがプロパティvold.encrypt_progressをerror_reboot_failedに設定し、ボタンを押して再起動するようユーザーに求めるメッセージが UI に表示されます。この問題が発生することは想定されていません。
デフォルトで暗号化されたデバイスの起動
これは、パスワードなしで暗号化されたデバイスを起動する場合に該当します。 Android 5.0 デバイスは初回起動時に暗号化されるため、パスワードは設定されていません。したがって、デフォルトの暗号化状態になります。
- パスワードなしで暗号化された
/dataを検出する/dataをマウントできずにフラグencryptableまたはforceencryptのどちらかが設定されていることから、Android デバイスが暗号化されていることを検出します。voldがvold.decryptをtrigger_default_encryptionに設定し、これによりdefaultcryptoサービスが開始されます。trigger_default_encryptionによって暗号化タイプがチェックされ、/dataの暗号化にパスワードが使用されているかどうかが確認されます。 - /data を復号する
ブロック デバイスを介して
dm-cryptデバイスを作成し、使用できるようにします。 - /data をマウントする
voldは復号された実際の/dataパーティションをマウントし、新しいパーティションを準備します。プロパティvold.post_fs_data_doneを 0 に設定してから、vold.decryptをtrigger_post_fs_dataに設定します。これにより、init.rcがpost-fs-dataコマンドを実行します。コマンドによって、必要なディレクトリまたはリンクが作成され、vold.post_fs_data_doneが 1 に設定されます。このプロパティが 1 に設定されると、
voldはプロパティvold.decryptをtrigger_restart_framework.に設定します。これにより、init.rcはクラスmainのサービスを再開するだけでなく、起動後初めてクラスlate_startのサービスも開始します。 - フレームワークを開始する
これで、フレームワークは復号された
/dataを使用してすべてのサービスを起動し、システムが使用可能になりました。
デフォルトの暗号化なしで暗号化されたデバイスの起動
これは、パスワードが設定されている暗号化されたデバイスを起動する場合に該当します。デバイスのパスワードには、PIN、パターン、またはパスワードを使用できます。
- パスワードで暗号化されたデバイスを検出する
フラグ
ro.crypto.state = "encrypted"により、Android デバイスが暗号化されていることを検出します。/dataがパスワードで暗号化されているため、voldはvold.decryptをtrigger_restart_min_frameworkに設定します。 - tmpfs をマウントする
initは、init.rcから渡されたパラメータを使用して、/dataに指定された初期マウント オプションを保存する 5 つのプロパティを設定します。voldは次のプロパティを使用して、暗号マッピングを設定します。ro.crypto.fs_typero.crypto.fs_real_blkdevro.crypto.fs_mnt_pointro.crypto.fs_optionsro.crypto.fs_flags(先頭に 0x がある ASCII の 8 桁の 16 進数)
- パスワードを要求するフレームワークを開始する
フレームワークが起動し、
vold.decryptがtrigger_restart_min_frameworkに設定されます。これは、tmpfs/dataディスクで起動していて、ユーザーのパスワードを取得する必要があることをフレームワークに伝えます。ただし、まずはディスクが適切に暗号化されていることを確認する必要があります。コマンド
cryptfs cryptocompleteがvoldに送信されます。voldは、暗号化が正常に完了した場合は 0、内部エラーが発生した場合は -1、暗号化が正常に完了しなかった場合は -2 を返します。voldは暗号メタデータのCRYPTO_ENCRYPTION_IN_PROGRESSフラグを調べることでこれを判断します。フラグが設定されている場合、暗号化プロセスは中断されており、デバイス上に使用可能なデータがありません。voldからエラーが返された場合、デバイスの再起動と初期化を求めるメッセージと、この操作を行うためのボタンが UI に表示されます。 - パスワードでデータを復号する
cryptfs cryptocompleteが成功すると、フレームワークによってディスク パスワードを要求する UI が表示されます。UI は、コマンドcryptfs checkpwをvoldに送信してパスワードを確認します。パスワードが正しい場合(復号された/dataを一時的な場所にマウントしてからマウント解除できるかどうかで判断する)、voldは復号されたブロック デバイスの名前をプロパティro.crypto.fs_crypto_blkdevに保存し、ステータス 0 を UI に返します。パスワードが正しくない場合は、UI に -1 が返されます。 - フレームワークを停止する
UI は暗号起動グラフィックを表示して、コマンド
cryptfs restartでvoldを呼び出します。voldがプロパティvold.decryptをtrigger_reset_mainに設定することにより、init.rcがclass_reset mainを実行します。これによりメインクラスのすべてのサービスが停止し、tmpfs/dataのマウントを解除できるようになります。 /dataをマウントする次に、
voldは復号された実際の/dataパーティションをマウントして新しいパーティションを準備します(最初のリリースでサポートされていないワイプ オプションで暗号化されている場合は、新しいパーティションが準備されない場合があります)。プロパティvold.post_fs_data_doneを 0 に設定してから、vold.decryptをtrigger_post_fs_dataに設定します。これにより、init.rcがpost-fs-dataコマンドを実行します。コマンドによって、必要なディレクトリまたはリンクが作成され、vold.post_fs_data_doneが 1 に設定されます。このプロパティが 1 に設定されると、voldはプロパティvold.decryptをtrigger_restart_frameworkに設定します。これにより、init.rcはクラスmainのサービスを再開するだけでなく、起動後初めてクラスlate_startのサービスも開始します。- 完全なフレームワークを開始する
これで、フレームワークは復号された
/dataファイルシステムを使用してすべてのサービスを起動し、システムが使用可能になります。
エラー
デバイスが復号に失敗する理由はいくつか考えられます。デバイスの起動には通常、一連のステップがあります。
- パスワードで暗号化されたデバイスを検出する
- tmpfs をマウントする
- パスワードを要求するフレームワークを開始する
ところが、フレームワークを開始した後で、デバイスに次のようなエラーが発生する場合があります。
- パスワードは一致しますが、データを復号できません
- ユーザーが間違ったパスワードを 30 回入力しました
これらのエラーが解決されない場合は、ユーザーに初期状態へのリセットが要求されます。
暗号化中に vold がエラーを検出し、データがまだ破棄されずにフレームワークが機能している場合、vold はプロパティ vold.encrypt_progress を error_not_encrypted に設定します。再起動を求めるプロンプトと、暗号化プロセスが開始されていないことを通知するアラートが UI に表示されます。フレームワークが破棄された後、進行状況バーの UI が動作する前にエラーが発生した場合、vold はシステムを再起動します。再起動に失敗すると、vold.encrypt_progress が error_shutting_down に設定されて -1 が返されますが、エラーが検出されることはありません。この状況が発生することは想定されていません。
暗号化中に vold がエラーを検出すると、vold.encrypt_progress が error_partially_encrypted に設定されて -1 が返されます。暗号化に失敗したことを示すメッセージと、ユーザーがデバイスを初期状態にリセットするためのボタンが UI に表示されます。
暗号化された鍵の保存
暗号化された鍵は、暗号メタデータに保存されます。ハードウェア バッキングは高信頼実行環境(TEE)の署名機能を使用して実装されます。以前は、ユーザーのパスワードと保存済みのソルトに scrypt を適用して、生成された鍵を使ってマスター鍵を暗号化していました。鍵をオフボックス攻撃から復元できるように、暗号化された鍵を保存済みの TEE 鍵で署名することでこのアルゴリズムを拡張します。scrypt をもう一度適用すると、この署名が適切な長さの鍵になります。その後、この鍵を使用してマスター鍵の暗号化と復号を行います。この鍵を保存するステップは次のとおりです。
- ランダムな 16 バイトのディスク暗号鍵(DEK)と 16 バイトのソルトを生成します。
- ユーザー パスワードとソルトに scrypt を適用して、32 バイトの中間鍵 1(IK1)を生成します。
- ハードウェアバインドされた秘密鍵(HBK)のサイズになるまで、IK1 に 0 バイトを追加します。 具体的には、00 || IK1 || 00..00(1 つの 0 バイト、32 の IK1 バイト、223 の 0 バイト)のように延長します。
- 延長した IK1 に HBK で署名し、256 バイトの IK2 を生成します。
- IK2 とソルト(ステップ 2 と同じソルト)に scrypt を適用して、32 バイトの IK3 を生成します。
- IK3 の最初の 16 バイトを KEK として、最後の 16 バイトを IV として使用します。
- AES_CBC、キー KEK、初期化ベクトル IV を使用して DEK を暗号化します。
パスワードの変更
ユーザーが設定でパスワードの変更または削除を選択すると、UI によってコマンド cryptfs changepw が vold に送信され、vold は新しいパスワードでディスク マスター鍵を再暗号化します。
暗号化のプロパティ
vold と init はプロパティを設定することで相互に通信します。暗号化に使用できるプロパティのリストは次のとおりです。
vold のプロパティ
| 特性 | 説明 |
|---|---|
vold.decrypt trigger_encryption |
パスワードなしでドライブを暗号化します。 |
vold.decrypt trigger_default_encryption |
ドライブがパスワードなしで暗号化されているかどうかを確認します。
パスワードなしの場合は復号してマウントし、パスワードで暗号化されている場合は vold.decrypt を trigger_restart_min_framework に設定します。 |
vold.decrypt trigger_reset_main |
ディスク パスワードを要求する UI をシャットダウンするために、vold によって設定されます。 |
vold.decrypt trigger_post_fs_data |
/data に必要なディレクトリなどを準備するために、vold によって設定されます。 |
vold.decrypt trigger_restart_framework |
実際のフレームワークとすべてのサービスを開始するために、vold によって設定されます。 |
vold.decrypt trigger_shutdown_framework |
フレームワーク全体をシャットダウンして暗号化を開始するために、vold によって設定されます。 |
vold.decrypt trigger_restart_min_framework |
ro.crypto.state の値に応じて、暗号化の進行状況バーを開始するか、パスワードの入力を求めるプロンプトを表示するために、vold によって設定されます。 |
vold.encrypt_progress |
フレームワークの起動時にこのプロパティが設定されていると、進行状況バー UI モードが開始されます。 |
vold.encrypt_progress 0 to 100 |
進行状況バー UI に割合の値が表示されます。 |
vold.encrypt_progress error_partially_encrypted |
進行状況バー UI に、暗号化の失敗を示すメッセージと、デバイスを出荷時設定にリセットする項目が表示されます。 |
vold.encrypt_progress error_reboot_failed |
進行状況バー UI に、暗号化の完了を示すメッセージと、デバイスを再起動するボタンが表示されます。このエラーの発生は想定されていません。 |
vold.encrypt_progress error_not_encrypted |
進行状況バー UI に、エラーが発生したことと、暗号化されたデータも失われたデータもないことを示すメッセージと、システムを再起動するボタンが表示されます。 |
vold.encrypt_progress error_shutting_down |
進行状況バー UI が動作しないため、エラーへの対処は示されません。このエラーが発生することはありません。 |
vold.post_fs_data_done 0 |
vold.decrypt を trigger_post_fs_data に設定する直前に vold によって設定されます。 |
vold.post_fs_data_done 1 |
タスク post-fs-data を完了した直後に、init.rc または init.rc によって設定されます。 |
init のプロパティ
| 特性 | 説明 |
|---|---|
ro.crypto.fs_crypto_blkdev |
vold コマンド restart で後で使用するために、vold コマンド checkpw によって設定されます。 |
ro.crypto.state unencrypted |
暗号化されていない /data ro.crypto.state encrypted でシステムが実行されていることを示すために、init によって設定されます。暗号化された /data でシステムが実行されていることを示すために、init によって設定されます。 |
|
これらの 5 つのプロパティは、init が init.rc から渡されたパラメータを使って /data をマウントするときに設定されます。vold はこれらを使用して暗号マッピングを設定します。 |
ro.crypto.tmpfs_options |
init が tmpfs /data ファイル システムをマウントするときに使用するオプションを使って init.rc によって設定されます。 |
init のアクション
on post-fs-data on nonencrypted on property:vold.decrypt=trigger_reset_main on property:vold.decrypt=trigger_post_fs_data on property:vold.decrypt=trigger_restart_min_framework on property:vold.decrypt=trigger_restart_framework on property:vold.decrypt=trigger_shutdown_framework on property:vold.decrypt=trigger_encryption on property:vold.decrypt=trigger_default_encryption