APK Signature Scheme v4

ב-Android 11 יש תמיכה בסכימת חתימה שתואמת לסטרימינג באמצעות APK Signature Scheme v4. החתימה בגרסה 4 מבוססת על עץ גיבוב Merkle שמחושב על כל הבייטים של קובץ ה-APK. הוא עוקב בדיוק אחרי המבנה של עץ הגיבוב של fs-verity (לדוגמה, הוספת אפסים למלח והוספת אפסים לבלוק האחרון). ב-Android 11 החתימה מאוחסנת בקובץ נפרד, <apk name>.apk.idsigחתימה בגרסה 4 מחייבת חתימה משלימה בגרסה 2 או 3.

תבנית קובץ

כל השדות המספריים הם בפורמט 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 נלקח מבלוק החתימה של גרסה 3 של ה-APK, או, אם הוא לא קיים, מבלוק הגרסה 2 (ראו 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 הוא עץ Merkle המלא של ה-APK, שמחושב כפי שמתואר במסמכי העזרה של fs-verity.

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

apksigner הכלי של Android SDK יוצר עכשיו את קובץ החתימה של V4 אם מפעילים אותו עם פרמטרים שמוגדרים כברירת מחדל. אפשר להשבית את החתימה של V4 באותו אופן שבו משביתים את שיטות החתימה האחרות. הוא יכול גם לאמת אם החתימה של V4 תקינה.

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

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

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

המרכיב של הטעינה המצטברת של נתונים אמור להעביר את החלק של עץ Merkle בחתימת ה-API המקורי של הטעינה של הנתונים.
package הפקודה של מעטפת השירות install-incremental מקבלת את קובץ החתימה של גרסה 4 שהוסר ממנו הקוד בקידוד base64, כפרמטר לכל קובץ שנוסף. יש לשלוח את עץ Merkle התואם ל-stdin של הפקודה.

apk_digest

apk_digest הוא סיכום התוכן הראשון שזמין בסדר הבא:

  1. V3, ‏ 1MB block, ‏ SHA2-512‏ (CONTENT_DIGEST_CHUNKED_SHA512),
  2. V3, ‏ בלוק של 4KB, ‏ SHA2-256‏ (CONTENT_DIGEST_VERITY_CHUNKED_SHA256),
  3. V3, ‏ בלוק של 1MB, ‏ SHA2-256‏ (CONTENT_DIGEST_CHUNKED_SHA256),
  4. V2, ‏ SHA2-512,
  5. V2, ‏ SHA2-256.

מידע נוסף זמין במאמר רצף עם קידומת אורך של חתימות עם קידומת אורך בסכמת החתימה של APK v3.

תהליך אימות APK v4
איור 1: תהליך אימות APK בגרסה 4

אימות ובדיקה

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

  • CtsIncrementalInstallHostTestCases
    • /android/cts/hostsidetests/incrementalinstall

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

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

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

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

כדי לבדוק את פורמט החתימה באמצעות Android SDK, צריך להגדיר סביבת פיתוח ולוודא שהשלמתם את ההטמעה של IncFS. לאחר מכן, מבצעים את הפלאש של ה-build במכשיר פיזי או במהדמה של היעד. צריך ליצור או לקבל קובץ APK קיים, ואז ליצור מפתח חתימה לניפוי באגים. לבסוף, חותמים על קובץ ה-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