ファイルベースの暗号化

Android 7.0 以降では、ファイルベースの暗号化(FBE)がサポートされています。ファイルベースの暗号化を使用すると、さまざまなファイルを異なる鍵で暗号化して、個別にロック解除できます。

この記事では、新しいデバイスでファイルベースの暗号化を有効にする方法と、システムアプリが Direct Boot API を使用して、可能な限り安全性の高い最適な操作性をユーザーに提供する仕組みについて説明します。

ダイレクト ブート

ファイルベースの暗号化により、Android 7.0 で導入されたダイレクト ブートという新機能が有効化されます。ダイレクト ブートでは、暗号化されたデバイスをロック画面に直接起動できます。これまで、フルディスク暗号化(FDE)を使用して暗号化されたデバイスでは、認証情報を提供しないとデータに一切アクセスできず、スマートフォンで最も基本的な操作を除くほとんどの操作を行えませんでした。たとえば、アラームを操作することも、ユーザー補助サービスを利用することもできず、緊急通報操作を除いてスマートフォンで通話を受信することもできません。

ファイルベースの暗号化(FBE)と、アプリが暗号化を認識できるようにする新しい API の導入により、限られたコンテキスト内でアプリが動作できるようになりました。ユーザーが認証情報を提供する前に、個人情報を保護したままでこの動作が実現します。

FBE 対応デバイスでは、デバイスの各ユーザーが 2 つの保存場所をアプリで使用できます。

  • デフォルトの保存先であり、ユーザーがデバイスのロックを解除した後にだけ使用できる認証情報暗号化(CE)ストレージ。
  • ダイレクト ブート モード中とユーザーがデバイスのロックを解除した後の両方で保存先として使用できるデバイス暗号化(DE)ストレージ。

暗号化が起動時のパスワードのみに基づくものではなくなり、この分離によって複数のユーザーを一度に保護できるため、仕事用プロファイルの安全性が高まります。

Direct Boot API を使用すると、暗号化対応のアプリがこれらの領域にアクセスできます。ロック画面に認証情報が最初に入力された際の応答として、または仕事用プロファイルでワーク チャレンジが提供された場合に、ユーザーの CE ストレージがロック解除されたことをアプリに通知できるようにアプリのライフサイクルが変更されています。Android 7.0 搭載デバイスは、FBE を実装しているかどうかにかかわらず、新しい API とライフサイクルをサポートする必要があります。ただし、FBE が実装されていない場合は、DE ストレージと CE ストレージが常にロック解除状態になります。

Android オープンソース プロジェクト(AOSP)で提供されている、Ext4 と F2FS のファイルシステムに対するファイルベースの暗号化の完全な実装は、要件を満たすデバイスでのみ有効です。FBE の使用を選択したメーカーは、使用するシステム オン チップ(SoC)に基づいて機能を最適化する方法を検討できます。

AOSP 内で対応の必要なパッケージはすべて、ダイレクトブート対応に更新されています。ただし、デバイス メーカーがこれらのアプリのカスタマイズ版を使用する場合、次のサービスを提供するダイレクトブート対応パッケージが最低限必要になります。

  • 電話サービスと電話アプリ
  • ロック画面にパスワードを入力するための入力方法

例とソース

Android にはファイルベースの暗号化のリファレンス実装が用意されており、vold(system/vold)によって Android のストレージ デバイスとボリュームを管理する機能が提供されます。FBE を追加すると、複数のユーザーの CE 鍵と DE 鍵の管理に対応したいくつかの新しいコマンドが vold に提供されます。カーネルでファイルベースの暗号化機能を使用するための主な変更に加え、ロック画面と SystemUI を含む多くのシステム パッケージが、FBE とダイレクト ブート機能をサポートするように変更されました。下記はその一例です。

  • AOSP 電話アプリ(packages/apps/Dialer)
  • 時計(packages/apps/DeskClock)
  • LatinIME(packages/inputmethods/LatinIME)*
  • 設定アプリ(packages/apps/Settings)*
  • SystemUI(frameworks/base/packages/SystemUI)*

* defaultToDeviceProtectedStorage マニフェスト属性を使用するシステムアプリ

暗号化に対応しているアプリとサービスのその他の例は、AOSP ソースツリーのフレームワークまたはパッケージ ディレクトリでコマンド mangrep directBootAware を実行することで確認できます。

依存関係

FBE の AOSP 実装を安全に使用するには、デバイスが次の依存関係を満たしている必要があります。

  • Ext4 暗号化または F2FS 暗号化のカーネル サポート
  • HAL バージョン 1.0 または 2.0 の Keymaster サポート。Keymaster 0.3 は、必要な機能を提供しなかったり、暗号鍵の保護が不十分であったりするため、サポートされていません。
  • Keymaster またはキーストアとゲートキーパーTrusted Execution Environment(TEE)に実装して DE 鍵を保護し、未承認の OS(デバイスにフラッシュされたカスタム OS)が簡単に DE 鍵を要求できないようにする必要があります。
  • 未承認のオペレーティング システムがデバイス暗号化の認証情報にアクセスできないようにするには、Keymaster の初期化にバインドされたハードウェア ルート オブ トラスト確認済みの起動が必要です。

: ストレージ ポリシーはフォルダとそのすべてのサブフォルダに適用されます。メーカーは暗号化しないコンテンツを、OTA フォルダと、システムを復号する鍵を保持するフォルダに制限する必要があります。ほとんどのコンテンツは、デバイス暗号化ストレージではなく認証情報暗号化ストレージに保存する必要があります。

実装

まずは、ダイレクト ブートのデベロッパー向けドキュメントに基づいて、アラーム、電話、ユーザー補助機能などのアプリを android:directBootAware にする必要があります。

カーネル サポート

Ext4 と F2FS の暗号化向けのカーネル サポートは、Android 共通カーネルのバージョン 3.18 以降で利用できます。バージョン 5.1 以降のカーネルでこのカーネル サポートを有効にする場合は、以下を使用します。

    CONFIG_FS_ENCRYPTION=y
    

バージョン 5.1 より前のカーネルの場合、デバイスの userdata ファイル システムが Ext4 であれば CONFIG_EXT4_ENCRYPTION=y を使用し、デバイスの userdata ファイル システムが F2FS であれば CONFIG_F2FS_FS_ENCRYPTION=y を使用します。

デバイスが Adoptable Storage をサポートする場合や、内部ストレージのメタデータ暗号化を使用する場合は、メタデータ暗号化に必要なカーネル構成オプションも有効にします。詳細については、メタデータ暗号化をご覧ください。

ファイルベース暗号化を高速化し、ユーザー エクスペリエンスを高めるため、デバイス メーカーは、Ext4 / F2FS 暗号化の機能サポートに加えて、暗号化アクセラレーションも有効にする必要があります。たとえば ARM64 ベースのデバイスでは、次のカーネル構成オプションを設定することによって ARMv8 CE(暗号化拡張機能)のアクセラレーションを有効にすることができます。

    CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
    CONFIG_CRYPTO_SHA2_ARM64_CE=y
    

デバイス メーカーは、さらなるパフォーマンスの向上と電力消費量の削減のために、ストレージ デバイスとの間でデータを伝送する際にデータの暗号化と復号を行う「インライン暗号化ハードウェア」の実装を検討することもできます。Android 共通カーネル(バージョン 4.14 以降)には、UFS ベースのストレージでハードウェアとベンダー ドライバのサポートが利用可能な場合にインライン暗号化を使用できるようにするフレームワークが搭載されています。インライン暗号化フレームワークを有効にするには、次のカーネル構成オプションを設定します。

    CONFIG_BLK_INLINE_ENCRYPTION=y
    CONFIG_FS_ENCRYPTION=y
    CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y
    CONFIG_SCSI_UFS_CRYPTO=y
    

ファイルベースの暗号化の有効化

デバイスで FBE を有効にする場合、内部ストレージ(userdata)で有効にする必要があります。これにより、Adoptable Storage でも FBE が自動的に有効化されます。ただし、Adoptable Storage での暗号化形式は、必要に応じてオーバーライドされることがあります。

内部ストレージ

FBE を有効にするには、userdatafstab 行の fs_mgr_flags 列にオプション fileencryption=contents_encryption_mode[:filenames_encryption_mode[:flags]] を追加します。このオプションでは、内部ストレージでの暗号化形式を定義します。オプションには最大 3 つのパラメータを含めることができます(コロンで区切って指定)。

  • contents_encryption_mode パラメータでは、ファイルの内容の暗号化に使用される暗号アルゴリズムを定義します。aes-256-xts または adiantum のいずれかを指定できます。
  • filenames_encryption_mode パラメータでは、ファイル名の暗号化に使用される暗号アルゴリズムを定義します。aes-256-ctsaes-256-hehadiantum のいずれかを指定できます。指定しない場合のデフォルトは、contents_encryption_modeaes-256-xts の場合は aes-256-ctscontents_encryption_modeadiantum の場合は adiantum です。
  • Android R で新たに導入された flags パラメータでは、フラグのリストを指定します(+ 記号で区切って指定)。次のフラグがサポートされています。
    • v1 フラグを指定するとバージョン 1 の暗号化ポリシーが、v2 フラグを指定するとバージョン 2 の暗号化ポリシーが選択されます。バージョン 2 の暗号化ポリシーでは、安全性と柔軟性に優れた鍵導出関数が使用されます。デフォルトは、Android R 以降を搭載して出荷されたデバイス(ro.product.first_api_level によって特定される)の場合は v2、Android 10 以前を搭載して出荷されたデバイスの場合は v1 です。
    • inlinecrypt_optimized フラグを指定すると、多数の鍵を効率的に処理できないインライン暗号化ハードウェア向けに最適化された暗号化形式が選択されます。これは、ファイルの内容の暗号化用の鍵を、ファイルごとに 1 つではなく、CE 鍵または DE 鍵ごとに 1 つだけ派生させることによって行われます。IV(初期化ベクトル)の生成は適宜調整されます。
    • wrappedkey_v0 フラグを指定すると、ハードウェアでラップされた鍵を使用できるようになります。このフラグを有効にした場合、FBE 鍵はソフトウェアでは生成されず、Keymaster で STORAGE_KEY タグを使用することによって生成されます。実際にカーネルに提供される各 FBE 鍵は、Keymaster からエクスポートされる STORAGE_KEY 鍵になります。そのため、ブートごとのエフェメラル鍵でラップされます。カーネルはその後、ラップされた鍵をインライン暗号化ハードウェアに直接提供します。実装が適切であれば、ラップ解除された鍵がシステムメモリ内に存在することはないため、ラップされた不正な鍵を再起動後に使用することはできません。このフラグを使用する場合、ハードウェアのサポート、STORAGE_KEY に対する Keymaster のサポート、カーネル ドライバのサポート、inlinecrypt マウント オプションが必要です。

インライン暗号化ハードウェアを使用していない場合、ほとんどのデバイスでは、fileencryption=aes-256-xts を設定することをおすすめします。インライン暗号化ハードウェアを使用している場合、ほとんどのデバイスでは、fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized を設定することをおすすめします。どの形式の AES アクセラレーションも使用していないデバイスでは、fileencryption=adiantum を設定することにより、AES の代わりに Adiantum を使用できます。

Android 10 以前を搭載して出荷されたデバイスでは、fileencryption=ice を指定することで、ファイルの内容の暗号化モードとして FSCRYPT_MODE_PRIVATE を使用することもできます。このモードは Android 共通カーネルでは実装されませんが、ベンダーがカスタムのカーネルパッチを使用して実装することはできました。このモードで生成されるディスク上の暗号化形式はベンダー固有だったのです。Android R 以降を搭載して出荷されるデバイスでは、このモードは使用できなくなりました。代わりに、標準の暗号化形式を使用する必要があります。

デフォルトでは、ファイルの内容の暗号化は Linux カーネルの暗号化 API を使用して行われます。インライン暗号化ハードウェアを使用する場合も、inlinecrypt マウント オプションを追加します。たとえば、完全な fstab 行は次のようになります。

    /dev/block/by-name/userdata /data f2fs nodev,noatime,nosuid,errors=panic,inlinecrypt wait,fileencryption=aes-256-xts:aes-256-cts:inlinecrypt_optimized
    

Adoptable Storage

Android 9 以降では、FBE と Adoptable Storage を組み合わせて使用できます。

また、userdata に対して fileencryption fstab オプションを指定すると、Adoptable Storage で FBE と メタデータ暗号化の両方が自動的に有効化されます。ただし Adoptable Storage では、PRODUCT_PROPERTY_OVERRIDES のプロパティを設定することで、FBE またはメタデータ暗号化(あるいはその両方)の形式をオーバーライドできます。

Android R 以降を搭載して出荷されたデバイスでは、次のプロパティを使用します。

  • ro.crypto.volume.options(Android R で新たに導入されたプロパティ): Adoptable Storage で FBE 暗号化形式を選択します。このプロパティは、fileencryption fstab オプションに対する引数と構文が同じであるため、同じデフォルトを使用します。このプロパティで使用する内容については、上記の fileencryption に関する推奨事項をご覧ください。
  • ro.crypto.volume.metadata.encryption: Adoptable Storage のメタデータ暗号化形式を選択します。詳細については、メタデータ暗号化をご覧ください。

Android 10 以前を搭載して出荷されたデバイスの場合、以下のプロパティを使用します。

  • ro.crypto.volume.contents_mode: ファイルの内容の暗号化モードを選択します。これは、ro.crypto.volume.options の最初のコロン区切りのフィールドと同じです。
  • ro.crypto.volume.filenames_mode: ファイル名の暗号化モードを選択します。これは、Android 10 以前を搭載して出荷されたデバイスのデフォルトが aes-256-heh であることを除き、ro.crypto.volume.options の 2 番目のコロン区切りのフィールドと同じです。ほとんどのデバイスでは、このデフォルトを明示的にオーバーライドして aes-256-cts にする必要があります。
  • ro.crypto.fde_algorithmro.crypto.fde_sector_size: Adoptable Storage のメタデータ暗号化形式を選択します。詳細については、メタデータ暗号化をご覧ください。

Keymaster との統合

鍵の生成とカーネル キーリングの管理は、vold によって行われます。FBE の AOSP 実装では、デバイスが Keymaster HAL バージョン 1.0 以降をサポートしている必要があります。以前のバージョンの Keymaster HAL はサポートされません。

最初の起動時にユーザー 0 の鍵が生成され、起動プロセスの初期段階でインストールされます。initon-post-fs フェーズが完了するまでに、Keymaster でリクエストを処理する準備ができている必要があります。Pixel デバイスでは、スクリプト ブロックを使用して /data のマウント前に Keymaster を起動させることでこれを実現します。

暗号化ポリシー

ファイルベースの暗号化では、ディレクトリ レベルで暗号化ポリシーが適用されます。デバイスの userdata パーティションが最初に作成されると、基本構造とポリシーが init スクリプトによって適用されます。これらのスクリプトは、最初のユーザー(ユーザー 0)の CE 鍵と DE 鍵の作成をトリガーし、さらにこれらの鍵で暗号化するディレクトリを定義します。追加のユーザーとプロファイルが作成されると、必要な鍵が追加で生成されてキーストアに保存されます。認証情報とデバイスの保存場所が作成され、暗号化ポリシーによってこれらの鍵がディレクトリにリンクされます。

Android R 以降では、暗号化ポリシーが一元管理された場所にハードコードされなくなりましたが、init スクリプトの mkdir コマンドへの引数で定義されるようになりました。システムの DE 鍵で暗号化されたディレクトリでは encryption=Require が使用されるのに対し、暗号化されていないディレクトリ(または、サブディレクトリがユーザー別の鍵で暗号化されたディレクトリ)では encryption=None が使用されます。

Android 10 では、暗号化ポリシーは次の場所にハードコードされていました。

/system/extras/libfscrypt/fscrypt_init_extensions.cpp

Android 9 以前では、次の場所にハードコードされていました。

/system/extras/ext4_utils/ext4_crypt_init_extensions.cpp

例外を追加して、特定のディレクトリが暗号化されないようにすることもできます。このような変更を行う場合、デバイス メーカーは暗号化されていないディレクトリを使用するアプリにのみアクセスを許可する SELinux ポリシーを含める必要があります。これにより、信頼できないアプリがすべて除外されます。

なお、従来の OTA 機能のサポートに含まれるのは既知の利用可能なユースケースのみです。

システムアプリでのダイレクト ブートのサポート

アプリをダイレクト ブート対応にする

システムアプリの迅速な移行を実現するために、アプリレベルで設定できる新しい属性が 2 つあります。defaultToDeviceProtectedStorage 属性はシステムアプリでのみ使用できます。directBootAware 属性はすべてのアプリに使用できます。

    <application
        android:directBootAware="true"
        android:defaultToDeviceProtectedStorage="true">
    

アプリレベルの directBootAware 属性は、簡単にアプリ内のすべてのコンポーネントを暗号化対応にする方法です。

defaultToDeviceProtectedStorage 属性は、CE ストレージではなく DE ストレージを指すように、デフォルトのアプリの保存場所をリダイレクトします。このフラグを使用するシステムアプリは、デフォルトの場所に保存されたすべてのデータを慎重に監査し、CE ストレージを使用するように機密データのパスを変更する必要があります。この方法を使用するデバイス メーカーは、保存するデータを慎重に調べて、個人情報が含まれていないことを確認する必要があります。

このモードを実行する場合は、次のシステム API を使用して、必要に応じて CE ストレージに保持されたコンテキストを明示的に管理できます。これは、デバイス保護ストレージに相当します。

  • Context.createCredentialProtectedStorageContext()
  • Context.isCredentialProtectedStorage()

複数のユーザーをサポートする

マルチユーザー環境の各ユーザーは個別の暗号鍵を取得します。すべてのユーザーが DE と CE の 2 つの鍵を取得します。ユーザー 0 は特別なユーザーであるため、最初にデバイスにログインする必要があります。これは、デバイス管理の使用に関係します。

暗号化対応アプリは、INTERACT_ACROSS_USERSINTERACT_ACROSS_USERS_FULL(アプリがデバイス上のすべてのユーザー間で動作できるようにする)によってユーザー間で動作します。ただしこれらのアプリがアクセスできるのは、すでにロック解除されているユーザーの CE で暗号化されたディレクトリのみです。

アプリは DE エリア間で自由に動作できますが、1 人のユーザーがロック解除されていても、デバイス上のすべてのユーザーがロック解除されているわけではありません。アプリはこのステータスを確認してから、これらの領域にアクセスする必要があります。

仕事用プロファイルの各ユーザー ID にも、DE と CE の 2 つの鍵があります。ワーク チャレンジが満たされると、プロファイルのユーザーがロック解除され、TEE 内の Keymaster はプロファイルの TEE 鍵を提供できます。

アップデートの処理

リカバリ パーティションは、ユーザーデータ パーティションの DE で保護されたストレージにアクセスできません。FBE を実装するデバイスでは、A/B システムアップデートを使用した OTA をサポートすることを強くおすすめします。通常の動作中に OTA を適用できるため、暗号化されたドライブ上のデータにリカバリがアクセスする必要はありません。

以前の OTA ソリューションを使用している場合は、リカバリが userdata パーティション上の OTA ファイルにアクセスする必要があります。

  1. userdata パーティションに最上位ディレクトリ(misc_ne など)を作成します。
  2. この最上位ディレクトリを暗号化ポリシーの例外に追加します(上の暗号化ポリシーを参照)。
  3. OTA パッケージを格納するディレクトリを最上位ディレクトリ内に作成します。
  4. SELinux ルールとファイル コンテキストを追加して、このフォルダとそのコンテンツへのアクセスを制御します。OTA アップデートを受信するプロセスまたはアプリのみが、このフォルダの読み取りと書き込みを行える必要があります。他のアプリやプロセスがこのフォルダにアクセスできないようにしてください。

検証

機能の実装バージョンが意図したとおりに動作することを確認するには、多数の CTS 暗号化テストを使用します。

基板用のカーネルがビルドされたら、x86 用もビルドして QEMU で実行し、kvm-xfstests を使用してテストする必要があります。Ext4 の場合は、以下を使用します。

    kvm-xfstests -c ext4/encrypt -g auto
    

F2FS の場合は、以下を使用します。

    kvm-xfstests -c f2fs/encrypt -g auto
    

また、デバイス メーカーは次の手動テストを実施することもできます。FBE が有効になっているデバイスで以下を実施します。

  • ro.crypto.state をチェックします
    • ro.crypto.state が暗号化されていることを確認します
  • ro.crypto.type をチェックします
    • ro.crypto.typefile に設定されていることを確認します

テストでは、メインユーザーに対して設定されたロック画面を使用して userdebug インスタンスを起動することもできます。次に、adb シェルでデバイスに接続し、su を使用して root になります。暗号化されたファイル名が /data/data に含まれていることを確認してください。含まれていない場合は、なんらかの異常があります。

AOSP 実装の詳細

このセクションでは、AOSP 実装の詳細とファイルベースの暗号化の仕組みについて説明します。デバイス メーカーが自社のデバイスで FBE とダイレクト ブートを使用するために、変更を加える必要はありません。

fscrypt 暗号化

AOSP 実装では、カーネルで「fscrypt」暗号化(ext4 と f2fs でサポートされている)が使用されます。これは通常、次のように設定されています。

  • XTS モードの AES-256 でファイルの内容を暗号化します
  • CBC-CTS モードの AES-256 でファイル名を暗号化します

Adiantum 暗号化もサポートされています。Adiantum 暗号化が有効になっている場合、ファイルの内容とファイル名はどちらも Adiantum で暗号化されます。

fscrypt について詳しくは、アップストリーム カーネルのドキュメントをご覧ください。

鍵の派生

512 ビットの鍵であるファイルベースの暗号鍵は、TEE に保持された別の鍵(256 ビット AES-GCM 鍵)で暗号化されてから保存されます。この TEE 鍵を使用するには、次の 3 つの要件を満たす必要があります。

  • 認証トークン
  • 伸長された認証情報
  • secdiscardable hash

認証トークンは、ユーザーが正常にログインしたときにゲートキーパーによって生成される、暗号化を用いて認証されたトークンです。正しい認証トークンが指定されていない場合、TEE は鍵の使用を拒否します。ユーザーに認証情報がない場合、認証トークンは使用されず、必要もありません。

伸長された認証情報とは、scrypt アルゴリズムによってソルト化と伸長が行われた後のユーザー認証情報です。実際には、認証情報はロック設定サービスで一旦ハッシュ化されてから、scrypt に渡すために vold に渡されます。これは、KM_TAG_APPLICATION_ID に適用されるすべての保証とともに TEE 内の鍵に暗号的にバインドされます。ユーザーに認証情報がない場合、伸長された認証情報は使用されず、必要もありません。

secdiscardable hash はランダムな 16 KB ファイルの 512 ビットのハッシュで、シードなどの鍵を再構築するために使用される他の情報と一緒に格納されます。このファイルは、鍵が削除されるか新しい方法で暗号化された場合に安全に削除されます。この強化された保護により、攻撃者が鍵を復元するには、安全に削除されたファイルのすべてのビットを復元する必要があります。これは、KM_TAG_APPLICATION_ID に適用されるすべての保証とともに TEE 内の鍵に暗号的にバインドされます。

ほとんどの場合、FBE 鍵では、暗号化で実際に使用されるサブ鍵(ファイル別またはモード別の鍵など)を生成するために、カーネル内で追加の鍵派生処理も実行されます。バージョン 2 の暗号化ポリシーでは、HKDF-SHA512 がこの処理に使用されます。