APK 署名スキーム v4

Android 11 は、APK 署名スキーム v4 によるストリーミング対応の署名スキームをサポートしています。v4 署名は、APK の全バイトに対して計算されたマークル ハッシュツリーに基づいています。これは fs-verity ハッシュツリーの構造に厳密に従っています(たとえば、ソルトの 0 パディングと最後のブロックの 0 パディングなど)。Android 11 では署名が別のファイル <apk name>.apk.idsig に保存されます。v4 署名には、補完的な v2 または v3 署名が必要です。

ファイル形式

数値フィールドはすべてリトル エンディアンです。すべてのフィールドのバイト数は sizeof() とまったく同じであり、暗黙的なパディングやアライメントは追加されません。

以下に、定義を簡略化するヘルパー構造体を示します。

template <class SizeT>
struct sized_bytes {
        SizeT size;
        byte bytes[size];
};

メインファイルのコンテンツ:

struct V4Signature {
        int32 version; // only version 2 is supported as of now
        sized_bytes<int32> hashing_info;
        sized_bytes<int32> signing_info;
        sized_bytes<int32> merkle_tree;  // optional
};

hashing_info は、ハッシュツリーの生成に使用されるパラメータであり、ルートハッシュです。

struct hashing_info.bytes {
    int32 hash_algorithm;    // only 1 == SHA256 supported
    int8 log2_blocksize;     // only 12 (block size 4096) supported now
    sized_bytes<int32> salt; // used exactly as in fs-verity, 32 bytes max
    sized_bytes<int32> raw_root_hash; // salted digest of the first Merkle tree page
};

signing_info は次の構造体です。

struct signing_info.bytes {
    sized_bytes<int32> apk_digest;  // used to match with the corresponding APK
    sized_bytes<int32> x509_certificate; // ASN.1 DER form
    sized_bytes<int32> additional_data; // a free-form binary data blob
    sized_bytes<int32> public_key; // ASN.1 DER, must match the x509_certificate
    int32 signature_algorithm_id; // see the APK v2 doc for the list
    sized_bytes<int32> signature;
};
  • apk_digest は APK の v3 署名ブロックから取得されるか、存在しない場合は v2 ブロックから取得されます(apk_digest を参照)

signature コードを作成して検証するには、次のデータをバイナリ blob にシリアル化し、署名 / 検証アルゴリズムに署名付きデータとして渡す必要があります。

struct V4DataForSigning {
        int32 size;
        int64 file_size; // the size of the file that's been hashed.
        hashing_info.hash_algorithm;
        hashing_info.log2_blocksize;
        hashing_info.salt;
        hashing_info.raw_root_hash;
        signing_info.apk_digest;
        signing_info.x509_certificate;
        signing_info.additional_data;
};
  1. merkle_tree は APK のマークルツリー全体で、fs-verity のドキュメントに記載されているとおりに計算されます。

プロデューサーとコンシューマー

デフォルトのパラメータを使用して実行した場合、apksigner Android SDK ツールは v4 署名ファイルを生成するようになりました。v4 署名は、他の署名スキームと同じ方法で無効にできます。また、v4 署名が有効かどうか検証することもできます。

adb は、adb install --incremental コマンドの実行時に .apk の次に .apk.idsig ファイルが存在すると想定します
また、デフォルトでは .idsig ファイルを使用して増分インストールを試み、見つからない場合または無効な場合は、通常のインストールに切り替えます。

インストール セッションが作成されると、ファイルをセッションに追加する際に、PackageInstaller 内の新しいストリーミング インストール API が stripped の v4 署名を別の引数として受け取ります。この時点で、signing_info は blob 全体として incfs に渡されます。incfs は、blob からルートハッシュを抽出します。

インストール セッションが commit されると、PackageManagerService は ioctl を実行し、signing_info blob を incfs から取得、解析して署名を検証します。

増分データローダ コンポーネントは、データローダのネイティブ API を介して署名のマークルツリー部分をストリーミングすることが求められます。
package サービス シェルコマンド install-incremental は、base64 としてエンコードされた stripped の v4 署名ファイルを、各追加ファイルのパラメータとして受け取ります。 対応するマークルツリーは、コマンドの stdin に送信する必要があります。

apk_digest

apk_digest は、使用可能な最初の整理されたコンテンツ ダイジェストです。

  1. V3、1 MB ブロック、SHA2-512(CONTENT_DIGEST_CHUNKED_SHA512)、
  2. V3、4 KB ブロック、SHA2-256(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)、
  3. V3、1 MB ブロック、SHA2-256(CONTENT_DIGEST_CHUNKED_SHA256)、
  4. V2、SHA2-512、
  5. V2、SHA2-256。

APK 署名スキーム v3 の長さプレフィックス付き署名の長さプレフィックス付きシーケンスをご覧ください。

apk 検証プロセス v4
図 1: APK 検証プロセス v4

検証とテスト

機能単体テストと CTS を使用して、実装を検証します。

  • CtsIncrementalInstallHostTestCases
    • /android/cts/hostsidetests/incrementalinstall

署名形式をテストする

署名形式をテストするには、開発環境を設定し、次の手動テストを実行します。

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

Android SDK(ADB と apksigner)を使用して署名形式をテストする

Android SDK を使用して署名形式をテストするには、開発環境を設定し、IncFS の実装が完了していることを確認します。次に、ターゲットとなる物理デバイスまたはエミュレータにビルドをフラッシュします。既存の APK を生成または取得してから、デバッグ署名鍵を作成する必要があります。最後に、build-tools フォルダから v4 署名形式を使用して APK に署名し、インストールします。

署名

$ ./apksigner sign --ks debug.keystore game.apk

インストール

$ ./adb install game.apk

このテストがある場所

/android/cts/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java