フルディスク暗号化とは、暗号化された鍵を使用して Android デバイス上のすべてのユーザーデータをエンコードするプロセスです。デバイスが暗号化されると、ユーザーが作成したすべてのデータはディスクにコミットされる前に自動的に暗号化され、すべての読み取りで呼び出しプロセスに戻す前にデータが自動的に復号されます。
フルディスク暗号化は Android 4.4 で導入されましたが、Android 5.0 では次の新機能が導入されました。
- 最初の起動に時間がかかるのを避けるために、データ パーティション上の使用済みのブロックのみを暗号化する高速な暗号化が作成されました。現在は ext4 と f2fs ファイルシステムのみが高速な暗号化をサポートしています。
- 初回起動時に暗号化を行う
forceencrypt
fstab フラグが追加されました。 - パターンとパスワードなしの暗号化のサポートが追加されました。
- 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
を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_type
ro.crypto.fs_real_blkdev
ro.crypto.fs_mnt_point
ro.crypto.fs_options
ro.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 に表示されます。
暗号化された鍵の保存
暗号化された鍵は、暗号メタデータに保存されます。ハードウェア バッキングは Trusted Execution Environment(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