APK Signature Scheme v4

‫Android 11 תומך בשיטת חתימה שמתאימה לסטרימינג עם APK signature scheme v4. החתימה בגרסה 4 מבוססת על עץ הגיבוב של מרקל (Merkle hash tree) שמחושב על כל הבייטים של ה-APK. הסכימה זהה בדיוק למבנה של עץ הגיבוב fs-verity (לדוגמה, ריפוד המלח באפסים וריפוד הבלוק האחרון באפסים). ב-Android 11, החתימה מאוחסנת בקובץ נפרד, <apk name>.apk.idsig. חתימת v4 דורשת חתימה משלימה מסוג v2 או v3.

תבנית קובץ

כל השדות המספריים הם בפורמט little endian. כל השדות תופסים בדיוק את מספר הבייטים שמופיע ב-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 נלקח מחסימת החתימה v3 של ה-APK. אם החסימה הזו לא קיימת, היא נלקחת מהחסימה 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;
};

merkle_tree הוא עץ מרקל המלא של ה-APK, שמחושב כמו שמתואר במסמכי התיעוד של fs-verity.

יצרנים וצרכנים

apksigner הכלי Android SDK יוצר את קובץ החתימה v4 אם מריצים אותו עם פרמטרים שמוגדרים כברירת מחדל. אפשר להשבית את חתימה בגרסה 4 באותה דרך שבה משביתים את שיטות החתימה האחרות. הכלי יכול גם לאמת אם חתימת v4 תקפה.

adb מצפה שקובץ .apk.idsig יהיה לצד קובץ ה-APK כשמריצים את הפקודה adb install --incremental. ‫adb גם משתמש בקובץ IDSIG כדי לנסות התקנה מצטברת כברירת מחדל, ואם הקובץ חסר או לא תקין, הוא חוזר להתקנה רגילה.

כשיוצרים סשן התקנה, ה-API החדש להתקנה בהזרמה ב-PackageInstaller מקבל את חתימת v4 המוסרת כארגומנט נפרד כשמוסיפים קובץ לסשן. בשלב הזה, signing_info מועבר ל-IncFS כבלוב שלם. מערכת IncFS מחלצת את הגיבוב הבסיסי מה-blob.

כשמבצעים קומיט של סשן ההתקנה, PackageManagerService מבצע קריאה של ioctl כדי לאחזר את ה-blob של signing_info מ-IncFS, מנתח אותו ומאמת את החתימה.

רכיב Incremental Data Loader מעביר בסטרימינג את החלק של עץ מרקל בחתימה דרך ה-API המקורי של כלי העברת הנתונים. הפקודה package service shell install-incremental מקבלת את קובץ החתימה v4 שבו הוסרו נתונים, בקידוד Base64, כפרמטר לכל קובץ שנוסף. צריך לשלוח את עץ מרקל המתאים ל-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

מידע נוסף זמין בקטע length-prefixed sequence of length-prefixed signer ב-APK signature scheme v3.

אימות ובדיקה

האיור הבא מציג את תהליך האימות של APK בגרסה 4:

תהליך אימות APK גרסה 4

איור 1. תהליך אימות APK גרסה 4.

מאמתים את ההטמעה באמצעות בדיקות יחידה של התכונה ו-CTS:

  • CtsIncrementalInstallHostTestCases
  • /android/cts/hostsidetests/incrementalinstall

בדיקת פורמט החתימה

כדי לבדוק את פורמט החתימה, מגדירים סביבת בנייה ומריצים את הבדיקות הידניות הבאות:

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

בדיקת פורמט החתימה באמצעות Android SDK‏ (ADB ו-apksigner)

כדי לבדוק את פורמט החתימה באמצעות Android SDK:

  1. מגדירים סביבת בנייה ומוודאים שהשלמתם את ההטמעה של IncFS.
  2. מצמידים את גרסת ה-build למכשיר פיזי או לאמולטור.
  3. יוצרים או מקבלים APK קיים ואז יוצרים מפתח חתימה לניפוי באגים.
  4. חותמים על ה-APK ומתקינים אותו בפורמט חתימה v4 מתיקיית build-tools.

    כניסה
    $ ./apksigner sign --ks debug.keystore game.apk

    התקנה
    $ ./adb install game.apk

    איפה אפשר למצוא את הניסויים האלה
    /android/cts/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java