フルディスク暗号化

フルディスク暗号化とは、暗号化された鍵を使用して 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、パターン、またはパスワードの制限を受ける場合があることに注意してください。

暗号化は initvold によって管理されます。 initvold を呼び出し、vold は init でイベントをトリガーするプロパティを設定します。システムの他の部分では、ステータスの報告、パスワードの要求、致命的なエラーの発生時に出荷時設定にリセットするかどうかの確認などのタスクを実行するプロパティがチェックされます。vold の暗号化機能の呼び出しには、コマンドライン ツール vdccryptfs コマンド(checkpwrestartenablecryptochangepwcryptocompleteverifypwsetfieldgetfieldmountdefaultencryptedgetpwtypegetpwclearpw)が使用されます。

/data を暗号化、復号、またはワイプするための /data のマウントはしないでください。ただし、ユーザー インターフェース(UI)を表示するには、フレームワークを開始して /data を実行する必要があります。この問題を解決するために、一時的なファイルシステムが /data にマウントされます。これにより、Android は必要に応じてパスワードの入力を求めたり、進行状況を表示したり、データのワイプを提案したりできます。この場合、一時ファイルシステムから実際の /data ファイルシステムに切り替えるために、一時ファイルシステム上でファイルを開いているすべてのプロセスを停止し、実際の /data ファイルシステムでそのプロセスを再開する必要が生じるという制限が課されます。このため、すべてのサービスは 3 つのグループ(coremainlate_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 デバイスの通常の初回起動です。

  1. forceencrypt フラグを使用して、暗号化されていないファイルシステムを検出する

    /data が暗号化されていませんが、forceencrypt によって強制されているため暗号化する必要があります。 /data のマウントを解除します。

  2. /data の暗号化を開始する

    vold.decrypt = "trigger_encryption" によって init.rc がトリガーされて、vold がパスワードなしで /data を暗号化します(新しいデバイスなのでパスワードは設定されていません)。

  3. tmpfs をマウントする

    voldro.crypto.tmpfs_options の tmpfs オプションを使用して tmpfs /data をマウントし、プロパティ vold.encrypt_progress を 0 に設定します。vold は暗号化されたシステムを起動するために tmpfs /data を準備し、プロパティ vold.decrypttrigger_restart_min_framework に設定します。

  4. 進行状況を表示するフレームワークを呼び出す

    実質的にデバイスには暗号化されるデータが存在せず、暗号化は短時間で実行されるため、実際に進行状況バーが表示されることはほとんどありません。進行状況の UI について詳しくは、既存のデバイスの暗号化をご覧ください。

  5. /data が暗号化されたら、フレームワークを停止する

    voldvold.decrypttrigger_default_encryption に設定し、これにより defaultcrypto サービスが開始されます(デフォルトで暗号化されたユーザーデータをマウントするために、以下のフローが開始されます)。trigger_default_encryption によって暗号化タイプがチェックされ、/data の暗号化にパスワードが使用されているかどうかが確認されます。Android 5.0 デバイスは初回起動時に暗号化されるため、パスワードは設定されていません。したがって、/data を復号してマウントします。

  6. /data をマウントする

    次に init は、init.rc に設定されている ro.crypto.tmpfs_options から取得したパラメータを使用して、tmpfs RAMDisk に /data をマウントします。

  7. フレームワークを開始する

    voldvold.decrypttrigger_restart_framework に設定し、通常の起動プロセスを続行します。

既存のデバイスの暗号化

これは、L に移行された暗号化されていない Android K 以前のデバイスを暗号化する場合に該当します。

ユーザーによって開始されるこのプロセスは、コードでは「インプレース暗号化」と呼ばれます。ユーザーがデバイスの暗号化を選択すると、暗号化処理を完了するのに十分な電力が確保されるように、UI は電池が完全に充電されて AC アダプターが接続されていることを確認します。

警告: 暗号化が終了する前にデバイスの電源が切れてシャットダウンすると、ファイルのデータは部分的に暗号化された状態になります。この場合はデバイスを初期状態にリセットする必要があり、データはすべて失われます。

インプレース暗号化を有効にするために、vold はループを開始して実際のブロック デバイスの各セクターを読み取り、暗号ブロック デバイスに書き込みます。vold はセクターの読み取りと書き込みを行う前に使用中であるかどうかを確認して、データがほとんどまたはまったくない新しいデバイスでの暗号化を高速化します。

デバイスの状態: ro.crypto.state = "unencrypted" を設定し、on nonencrypted init トリガーを実行して起動を続行します。

  1. パスワードを確認する

    UI はコマンド cryptfs enablecrypto inplacepasswd はユーザーのロック画面のパスワード)で vold を呼び出します。

  2. フレームワークを停止する

    vold はエラーをチェックし、暗号化できない場合は -1 を返してログに理由を出力します。暗号化できる場合は、プロパティ vold.decrypttrigger_shutdown_framework に設定します。これにより、init.rclate_start クラスと main クラスのサービスを停止します。

  3. 暗号フッターを作成する
  4. パンくずリストファイルを作成する
  5. 再起動する
  6. パンくずリストファイルを検出する
  7. /data の暗号化を開始する

    次に vold が暗号マッピングを設定します。これにより、実際のブロック デバイスにマッピングされる仮想暗号ブロック デバイスが作成され、各セクターを書き込みのたびに暗号化し、読み込みのたびに復号します。その後、vold は暗号メタデータを作成して書き出します。

  8. 暗号化中に tmpfs をマウントする

    voldro.crypto.tmpfs_options の tmpfs オプションを使用して tmpfs /data をマウントし、プロパティ vold.encrypt_progress を 0 に設定します。vold は暗号化されたシステムを起動するために tmpfs /data を準備し、プロパティ vold.decrypttrigger_restart_min_framework に設定します。

  9. 進行状況を表示するフレームワークを呼び出す

    trigger_restart_min_framework によって init.rcmain クラスのサービスを開始します。フレームワークで vold.encrypt_progress が 0 に設定されると、進行状況バーの UI が表示され、5 秒ごとにプロパティが照会されて進行状況バーが更新されます。暗号化ループは、暗号化されたパーティションの割合が変わるたびに vold.encrypt_progress を更新します。

  10. /data が暗号化されたら、暗号フッターを更新する

    /data が正常に暗号化されると、vold はメタデータのフラグ ENCRYPTION_IN_PROGRESS をクリアします。

    デバイスのロック解除が成功すると、パスワードを使用してマスター鍵が暗号化され、暗号フッターが更新されます。

    なんらかの理由で再起動が失敗した場合、vold がプロパティ vold.encrypt_progresserror_reboot_failed に設定し、ボタンを押して再起動するようユーザーに求めるメッセージが UI に表示されます。この問題が発生することは想定されていません。

デフォルトで暗号化されたデバイスの起動

これは、パスワードなしで暗号化されたデバイスを起動する場合に該当します。 Android 5.0 デバイスは初回起動時に暗号化されるため、パスワードは設定されていません。したがって、デフォルトの暗号化状態になります。

  1. パスワードなしで暗号化された /data を検出する

    /data をマウントできずにフラグ encryptable または forceencrypt のどちらかが設定されていることから、Android デバイスが暗号化されていることを検出します。

    voldvold.decrypttrigger_default_encryption に設定し、これにより defaultcrypto サービスが開始されます。trigger_default_encryption によって暗号化タイプがチェックされ、/data の暗号化にパスワードが使用されているかどうかが確認されます。

  2. /data を復号する

    ブロック デバイスを介して dm-crypt デバイスを作成し、使用できるようにします。

  3. /data をマウントする

    vold は復号された実際の /data パーティションをマウントし、新しいパーティションを準備します。プロパティ vold.post_fs_data_done を 0 に設定してから、vold.decrypttrigger_post_fs_data に設定します。これにより、init.rcpost-fs-data コマンドを実行します。コマンドによって、必要なディレクトリまたはリンクが作成され、vold.post_fs_data_done が 1 に設定されます。

    このプロパティが 1 に設定されると、vold はプロパティ vold.decrypttrigger_restart_framework. に設定します。これにより、init.rc はクラス main のサービスを再開するだけでなく、起動後初めてクラス late_start のサービスも開始します。

  4. フレームワークを開始する

    これで、フレームワークは復号された /data を使用してすべてのサービスを起動し、システムが使用可能になりました。

デフォルトの暗号化なしで暗号化されたデバイスの起動

これは、パスワードが設定されている暗号化されたデバイスを起動する場合に該当します。デバイスのパスワードには、PIN、パターン、またはパスワードを使用できます。

  1. パスワードで暗号化されたデバイスを検出する

    フラグ ro.crypto.state = "encrypted" により、Android デバイスが暗号化されていることを検出します。

    /data がパスワードで暗号化されているため、voldvold.decrypttrigger_restart_min_framework に設定します。

  2. tmpfs をマウントする

    init は、init.rc から渡されたパラメータを使用して、/data に指定された初期マウント オプションを保存する 5 つのプロパティを設定します。vold は次のプロパティを使用して、暗号マッピングを設定します。

    1. ro.crypto.fs_type
    2. ro.crypto.fs_real_blkdev
    3. ro.crypto.fs_mnt_point
    4. ro.crypto.fs_options
    5. ro.crypto.fs_flags (先頭に 0x がある ASCII の 8 桁の 16 進数)
  3. パスワードを要求するフレームワークを開始する

    フレームワークが起動し、vold.decrypttrigger_restart_min_framework に設定されます。これは、tmpfs /data ディスクで起動していて、ユーザーのパスワードを取得する必要があることをフレームワークに伝えます。

    ただし、まずはディスクが適切に暗号化されていることを確認する必要があります。コマンド cryptfs cryptocompletevold に送信されます。vold は、暗号化が正常に完了した場合は 0、内部エラーが発生した場合は -1、暗号化が正常に完了しなかった場合は -2 を返します。vold は暗号メタデータの CRYPTO_ENCRYPTION_IN_PROGRESS フラグを調べることでこれを判断します。フラグが設定されている場合、暗号化プロセスは中断されており、デバイス上に使用可能なデータがありません。vold からエラーが返された場合、デバイスの再起動と初期化を求めるメッセージと、この操作を行うためのボタンが UI に表示されます。

  4. パスワードでデータを復号する

    cryptfs cryptocomplete が成功すると、フレームワークによってディスク パスワードを要求する UI が表示されます。UI は、コマンド cryptfs checkpwvold に送信してパスワードを確認します。パスワードが正しい場合(復号された /data を一時的な場所にマウントしてからマウント解除できるかどうかで判断する)、vold は復号されたブロック デバイスの名前をプロパティ ro.crypto.fs_crypto_blkdev に保存し、ステータス 0 を UI に返します。パスワードが正しくない場合は、UI に -1 が返されます。

  5. フレームワークを停止する

    UI は暗号起動グラフィックを表示して、コマンド cryptfs restartvold を呼び出します。vold がプロパティ vold.decrypttrigger_reset_main に設定することにより、init.rcclass_reset main を実行します。これによりメインクラスのすべてのサービスが停止し、tmpfs /data のマウントを解除できるようになります。

  6. /data をマウントする

    次に、vold は復号された実際の /data パーティションをマウントして新しいパーティションを準備します(最初のリリースでサポートされていないワイプ オプションで暗号化されている場合は、新しいパーティションが準備されない場合があります)。プロパティ vold.post_fs_data_done を 0 に設定してから、vold.decrypttrigger_post_fs_data に設定します。これにより、init.rcpost-fs-data コマンドを実行します。コマンドによって、必要なディレクトリまたはリンクが作成され、vold.post_fs_data_done が 1 に設定されます。このプロパティが 1 に設定されると、vold はプロパティ vold.decrypttrigger_restart_framework に設定します。これにより、init.rc はクラス main のサービスを再開するだけでなく、起動後初めてクラス late_start のサービスも開始します。

  7. 完全なフレームワークを開始する

    これで、フレームワークは復号された /data ファイルシステムを使用してすべてのサービスを起動し、システムが使用可能になります。

エラー

デバイスが復号に失敗する理由はいくつか考えられます。デバイスの起動には通常、一連のステップがあります。

  1. パスワードで暗号化されたデバイスを検出する
  2. tmpfs をマウントする
  3. パスワードを要求するフレームワークを開始する

ところが、フレームワークを開始した後で、デバイスに次のようなエラーが発生する場合があります。

  • パスワードは一致しますが、データを復号できません
  • ユーザーが間違ったパスワードを 30 回入力しました

これらのエラーが解決されない場合は、ユーザーに初期状態へのリセットが要求されます

暗号化中に vold がエラーを検出し、データがまだ破棄されずにフレームワークが機能している場合、vold はプロパティ vold.encrypt_progress error_not_encrypted に設定します。再起動を求めるプロンプトと、暗号化プロセスが開始されていないことを通知するアラートが UI に表示されます。フレームワークが破棄された後、進行状況バーの UI が動作する前にエラーが発生した場合、vold はシステムを再起動します。再起動に失敗すると、vold.encrypt_progresserror_shutting_down に設定されて -1 が返されますが、エラーが検出されることはありません。この状況が発生することは想定されていません。

暗号化中に vold がエラーを検出すると、vold.encrypt_progresserror_partially_encrypted に設定されて -1 が返されます。暗号化に失敗したことを示すメッセージと、ユーザーがデバイスを初期状態にリセットするためのボタンが UI に表示されます。

暗号化された鍵の保存

暗号化された鍵は、暗号メタデータに保存されます。ハードウェア バッキングは高信頼実行環境(TEE)の署名機能を使用して実装されます。以前は、ユーザーのパスワードと保存済みのソルトに scrypt を適用して、生成された鍵を使ってマスター鍵を暗号化していました。鍵をオフボックス攻撃から復元できるように、暗号化された鍵を保存済みの TEE 鍵で署名することでこのアルゴリズムを拡張します。scrypt をもう一度適用すると、この署名が適切な長さの鍵になります。その後、この鍵を使用してマスター鍵の暗号化と復号を行います。この鍵を保存するステップは次のとおりです。

  1. ランダムな 16 バイトのディスク暗号鍵(DEK)と 16 バイトのソルトを生成します。
  2. ユーザー パスワードとソルトに scrypt を適用して、32 バイトの中間鍵 1(IK1)を生成します。
  3. ハードウェアバインドされた秘密鍵(HBK)のサイズになるまで、IK1 に 0 バイトを追加します。 具体的には、00 || IK1 || 00..00(1 つの 0 バイト、32 の IK1 バイト、223 の 0 バイト)のように延長します。
  4. 延長した IK1 に HBK で署名し、256 バイトの IK2 を生成します。
  5. IK2 とソルト(ステップ 2 と同じソルト)に scrypt を適用して、32 バイトの IK3 を生成します。
  6. IK3 の最初の 16 バイトを KEK として、最後の 16 バイトを IV として使用します。
  7. AES_CBC、キー KEK、初期化ベクトル IV を使用して DEK を暗号化します。

パスワードの変更

ユーザーが設定でパスワードの変更または削除を選択すると、UI によってコマンド cryptfs changepwvold に送信され、vold は新しいパスワードでディスク マスター鍵を再暗号化します。

暗号化のプロパティ

voldinit はプロパティを設定することで相互に通信します。暗号化に使用できるプロパティのリストは次のとおりです。

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.decrypttrigger_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 によって設定されます。

ro.crypto.fs_type
ro.crypto.fs_real_blkdev
ro.crypto.fs_mnt_point
ro.crypto.fs_options
ro.crypto.fs_flags

これらの 5 つのプロパティは、initinit.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