الإصدار 4 من مخطّط توقيع حِزم APK

يتوافق نظام التشغيل Android 11 مع مخطّط توقيع متوافق مع البث باستخدام الإصدار 4 من مخطّط توقيع حِزم APK. يستند التوقيع بالإصدار 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. إذا لم يكن هذا الحظر متوفّرًا، سيتم استخراجه من الحظر 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 هو شجرة Merkle الكاملة لحزمة APK، ويتم احتسابها كما هو موضّح في مستند fs-verity.

المنتجون والمستهلكون

تنشئ أداة apksigner Android SDK ملف التوقيع بالإصدار 4 إذا نفّذتها باستخدام المَعلمات التلقائية. يمكنك إيقاف التوقيع باستخدام الإصدار 4 بالطريقة نفسها التي يتم بها إيقاف أنظمة التوقيع الأخرى. يمكن للأداة أيضًا التحقّق مما إذا كانت توقيع الإصدار 4 صالحًا.

يتوقّع adb أن يكون الملف .apk.idsig متوفّرًا بجانب حزمة APK عند تنفيذ الأمر adb install --incremental. يستخدم adb أيضًا ملف IDSIG لتجربة التثبيت التزايدي تلقائيًا، ويعود إلى التثبيت العادي في حال كان الملف مفقودًا أو غير صالح.

عند إنشاء جلسة تثبيت، تقبل واجهة برمجة التطبيقات الجديدة الخاصة بالتثبيت أثناء البث في PackageInstaller توقيع الإصدار 4 المجرّد كوسيطة منفصلة عند إضافة ملف إلى الجلسة. في هذه المرحلة، يتم تمرير signing_info إلى IncFS ككائن ثنائي كبير الحجم. تستخرج IncFS تجزئة الجذر من الكائن الثنائي الكبير.

عندما يتم تنفيذ جلسة التثبيت، يرسل PackageManagerService طلب ioctl لاسترداد كائن signing_info الثنائي الكبير (blob) من IncFS، ويحلّله، ويتحقّق من التوقيع.

يبث مكوّن Incremental Data Loader جزء شجرة Merkle من التوقيع من خلال واجهة برمجة التطبيقات الأصلية لأداة تحميل البيانات. يقبل packageأمر shell الخاص بالخدمة install-incremental ملف توقيع v4 الذي تمت إزالة مساحاته البيضاء والمشفّر بتنسيق Base64 كمعلَمة لكل ملف تمت إضافته. يجب إرسال شجرة Merkle المقابلة إلى stdin الخاص بالأمر.

apk_digest

apk_digest هي أول خلاصة محتوى متاحة بالترتيب:

  1. الإصدار 3، حجم الحزمة 1 ميغابايت، خوارزمية SHA2-512 (CONTENT_DIGEST_CHUNKED_SHA512)
  2. الإصدار 3، كتلة بحجم 4 كيلوبايت، خوارزمية SHA2-256 (CONTENT_DIGEST_VERITY_CHUNKED_SHA256)
  3. الإصدار 3، حجم الحزمة 1 ميغابايت، خوارزمية SHA2-256 (CONTENT_DIGEST_CHUNKED_SHA256)
  4. الإصدار 2، SHA2-512
  5. الإصدار 2، SHA2-256

راجِع تسلسل الموقّع الذي يسبقه الطول في الإصدار 3 من مخطّط توقيع حزمة APK.

التحقّق من صحة الإجراءات واختبارها

يوضّح الشكل التالي عملية التحقّق من صحة حزمة APK الإصدار 4:

عملية التحقّق من صحة حزمة APK الإصدار 4

الشكل 1: عملية التحقّق من صحة حِزم APK الإصدار 4

تحقَّق من صحة التنفيذ باستخدام اختبارات الوحدات الخاصة بالميزات وCTS:

  • CtsIncrementalInstallHostTestCases
  • /android/cts/hostsidetests/incrementalinstall

اختبار تنسيق التوقيع

لاختبار تنسيق التوقيع، اضبط بيئة إنشاء ونفِّذ الاختبارات اليدوية التالية:

$ atest PackageManagerShellCommandTest
PackageManagerShellCommandIncrementalTest

اختبار تنسيق التوقيع باستخدام حزمة تطوير البرامج (SDK) لنظام التشغيل Android (أداة تصحيح أخطاء Android وأداة apksigner)

استخدِم هذه العملية لاختبار تنسيق التوقيع باستخدام حزمة تطوير البرامج (SDK) لنظام التشغيل Android:

  1. إعداد بيئة إنشاء والتأكّد من إكمال عملية تنفيذ IncFS
  2. نقل الإصدار إلى جهاز فعلي أو محاكي مستهدف
  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