קישור לגרסה

ב-Keymaster 1, כל המפתחות של Keymaster היו מקושרים באופן קריפטוגרפי לRoot of Trust (בסיס האמון) של המכשיר, או למפתח האתחול המאומת. ב-Keymaster 2 ו-3, כל המפתחות מקושרים גם למערכת ההפעלה ולרמת התיקון של קובץ האימג' של המערכת. כך תוקף שמגלה נקודת חולשה בגרסה ישנה של מערכת או תוכנת TEE לא יכול להחזיר מכשיר לגרסה הפגיעה ולהשתמש במפתחות שנוצרו בגרסה החדשה יותר. בנוסף, כשמשתמשים במפתח עם גרסה ורמת תיקון מסוימות במכשיר ששודרג לגרסה חדשה יותר או לרמת תיקון חדשה יותר, המפתח משודרג לפני שאפשר להשתמש בו, והגרסה הקודמת של המפתח מבוטלת. כך, כשמשדרגים את המכשיר, המקשים *מתקדמים* יחד עם המכשיר, אבל אם חוזרים לגרסה קודמת של המכשיר, אי אפשר להשתמש במקשים.

כדי לתמוך במבנה המודולרי של Treble ולבטל את הקישור של system.img ל-boot.img, ב-Keymaster 4 שינינו את מודל הקישור של גרסת המפתח כך שיהיה בו רמות תיקון נפרדות לכל מחיצה. כך אפשר לעדכן כל מחיצה בנפרד, ועדיין לספק הגנה מפני חזרה לאחור.

ב-Android 9, לכל אחת מהמחיצות boot, ‏ system ו-vendor יש רמת תיקון משלה.

  • במכשירים עם Android Verified Boot‏ (AVB) אפשר לשמור את כל רמות התיקונים ואת גרסת המערכת ב-vbmeta, כדי שה-bootloader יוכל לספק אותם ל-Keymaster. במחיצות מחוברות, פרטי הגרסה של המחיצה נמצאים ב-vbmeta המקושר. באופן כללי, פרטי הגרסה צריכים להופיע בשדה vbmeta struct שמכיל את נתוני האימות (hash או hashtree) של מחיצה נתונה.
  • במכשירים ללא AVB:
    • הטמעות של הפעלה מאומתת צריכות לספק גיבוב של המטא-נתונים של הגרסה לתוכנת האתחול, כדי שתוכנת האתחול תוכל לספק את הגיבוב ל-Keymaster.
    • boot.img יכול להמשיך לאחסן את רמת התיקון בכותרת
    • system.img יכול להמשיך לאחסן את רמת התיקון ואת גרסת מערכת ההפעלה בנכסים לקריאה בלבד
    • vendor.img שומר את רמת התיקון במאפיין ro.vendor.build.version.security_patch לקריאה בלבד.
    • מנהל האתחול יכול לספק ל-keymaster גיבוב של כל הנתונים שאומתו על ידי אתחול מאומת.
  • ב-Android 9, משתמשים בתגים הבאים כדי לספק את פרטי הגרסה של המחיצות הבאות:
    • VENDOR_PATCH_LEVEL: מחיצה vendor
    • BOOT_PATCH_LEVEL: מחיצה boot
    • OS_PATCH_LEVEL ו-OS_VERSION: מחיצה system. (OS_VERSION הוסר מהכותרת boot.img.
  • בהטמעות של Keymaster, צריך להתייחס לכל רמות התיקונים בנפרד. אפשר להשתמש במפתחות אם כל פרטי הגרסה תואמים לערכים המשויכים למפתח, ו-IKeymaster::upgradeDevice() מתגלגל לרמת תיקון גבוהה יותר במקרה הצורך.

שינויים ב-HAL

כדי לתמוך בקישור גרסאות ובאימות גרסאות, נוספו ל-Android 7.1 התגים Tag::OS_VERSION ו-Tag::OS_PATCHLEVEL והשיטות configure ו-upgradeKey. תגי הגרסה מתווספים באופן אוטומטי על ידי הטמעות של Keymaster 2 ואילך לכל המפתחות שנוצרים (או מתעדכנים) חדשים. בנוסף, כל ניסיון להשתמש במפתח שאין לו גרסת מערכת הפעלה או רמת תיקון שתואמים לגרסת מערכת ההפעלה או לרמת התיקון הנוכחית של המערכת, נדחה עם ErrorCode::KEY_REQUIRES_UPGRADE.

Tag::OS_VERSION הוא ערך UINT שמייצג את החלקים הראשיים, המשניים והמשניים המשניים של גרסת מערכת Android בתור MMmmss, כאשר MM היא הגרסה הראשית, mm היא הגרסה המשנית ו-ss היא הגרסה המשנית המשנית. לדוגמה, 6.1.2 יוצג כ-060102.

Tag::OS_PATCHLEVEL הוא ערך UINT שמייצג את השנה והחודש של העדכון האחרון למערכת בפורמט YYYYMM, כאשר YYYY הוא השנה בת ארבע ספרות ו-MM הוא החודש בן שתי הספרות. לדוגמה, מרץ 2016 יצוין בתור 201603.

UpgradeKey

כדי לאפשר שדרוג של מפתחות לגרסה החדשה של מערכת ההפעלה ולרמת התיקון החדשה של קובץ האימג' של המערכת, ב-Android 7.1 נוספה השיטה upgradeKey ל-HAL:

Keymaster 3

    upgradeKey(vec keyBlobToUpgrade, vec upgradeParams)
        generates(ErrorCode error, vec upgradedKeyBlob);

Keymaster 2

keymaster_error_t (*upgrade_key)(const struct keymaster2_device* dev,
    const keymaster_key_blob_t* key_to_upgrade,
    const keymaster_key_param_set_t* upgrade_params,
    keymaster_key_blob_t* upgraded_key);
  • dev הוא מבנה המכשיר
  • keyBlobToUpgrade הוא המפתח שצריך לשדרג
  • upgradeParams הם פרמטרים שנדרשים לשדרוג המפתח. אלה כוללים את Tag::APPLICATION_ID ואת Tag::APPLICATION_DATA, שנדרשים כדי לפענח את ה-blob של המפתח, אם הם סופקו במהלך היצירה.
  • upgradedKeyBlob הוא פרמטר הפלט, שמשמש להחזרת ה-blob החדש של המפתח.

אם upgradeKey נקרא עם blob של מפתח שאי אפשר לנתח או שהוא לא תקין מסיבה אחרת, הוא מחזיר את הערך ErrorCode::INVALID_KEY_BLOB. אם היא נקראת עם מפתח שרמת התיקון שלו גדולה מהערך הנוכחי במערכת, היא מחזירה את הערך ErrorCode::INVALID_ARGUMENT. אם היא נקראת עם מפתח שגרסת מערכת ההפעלה שלו גדולה מהערך הנוכחי במערכת, והערך במערכת שונה מאפס, היא מחזירה את הערך ErrorCode::INVALID_ARGUMENT. מותר לשדרג גרסת מערכת הפעלה ממספר שאינו אפס לאפס. במקרה של שגיאות בתקשורת עם העולם המאובטח, הפונקציה מחזירה ערך שגיאה מתאים (לדוגמה, ErrorCode::SECURE_HW_ACCESS_DENIED,‏ ErrorCode::SECURE_HW_BUSY). אחרת, היא מחזירה את הערך ErrorCode::OK ומחזירה blob מפתח חדש ב-upgradedKeyBlob.

keyBlobToUpgrade נשאר בתוקף אחרי הקריאה ל-upgradeKey, וניתן להשתמש בו שוב באופן תיאורטי אם המכשיר יופחת לרמה נמוכה יותר. בפועל, בדרך כלל מתבצעת קריאה ל-deleteKey על ה-blob של keyBlobToUpgrade זמן קצר אחרי הקריאה ל-upgradeKey. אם ב-keyBlobToUpgrade היה תג Tag::ROLLBACK_RESISTANT, אז הוא אמור להיות גם ב-upgradedKeyBlob (והוא אמור להיות עמיד לרולבק).

תצורה מאובטחת

כדי להטמיע קישור גרסאות, ל-TA של מפתח המאסטר צריכה להיות דרך לקבל באופן מאובטח את גרסת מערכת ההפעלה הנוכחית ואת רמת התיקון (פרטי הגרסה), וכדי לוודא שהמידע שהוא מקבל תואם מאוד למידע על המערכת שפועלת.

כדי לתמוך בהעברה מאובטחת של פרטי הגרסה לטכנאי התמיכה, נוסף שדה OS_VERSION לכותרת של קובץ האימג' להפעלה. סקריפט ה-build של קובץ האימג' לאתחול מאכלס את השדה הזה באופן אוטומטי. יצרני ציוד מקורי (OEM) ומטמיעים של תוסף אבטחה (TA) של Keymaster צריכים לעבוד יחד כדי לשנות את מנהלי האתחול של המכשיר כך שיוציאו את פרטי הגרסה מקובץ האימג' של האתחול ויעבירו אותם לתוסף האבטחה לפני שהמערכת הלא מאובטחת מופעלת. כך תהיה לכם ודאות שמתקפת APT לא תוכל להפריע להקצאה של פרטי הגרסה ל-TA.

בנוסף, צריך לוודא שבקובץ האימג' של המערכת יש את אותם פרטי גרסה כמו בקובץ האימג' של האתחול. לשם כך, הוספנו את שיטת ההגדרה ל-HAL של Keymaster:

keymaster_error_t (*configure)(const struct keymaster2_device* dev,
  const keymaster_key_param_set_t* params);

הארגומנט params מכיל את Tag::OS_VERSION ו-Tag::OS_PATCHLEVEL. לקוחות של keymaster2 קוראים לשיטה הזו אחרי פתיחת ה-HAL, אבל לפני קריאה לשיטות אחרות. אם מתבצעת קריאה לשיטה אחרת לפני configure, ה-TA מחזיר את הערך ErrorCode::KEYMASTER_NOT_CONFIGURED.

בפעם הראשונה ש-configure נקרא אחרי שהמכשיר מופעל, הוא אמור לוודא שמידע הגרסה שסופק תואם למידע שסופק על ידי מנהל האתחול. אם פרטי הגרסה לא תואמים, configure מחזירה את הערך ErrorCode::INVALID_ARGUMENT וכל שאר השיטות של Keymaster ממשיכות להחזיר את הערך ErrorCode::KEYMASTER_NOT_CONFIGURED. אם המידע תואם, הפונקציה configure מחזירה את הערך ErrorCode::OK ושאר השיטות של Keymaster מתחילות לפעול כרגיל.

קריאות חוזרות ל-configure מחזירות את אותו ערך שהתקבל בקריאה הראשונה, ולא משנות את המצב של keymaster.

מכיוון ש-configure נקרא על ידי המערכת שהתוכן שלה הוא אמור לאמת, יש חלון הזדמנויות צר לתוקף לפגוע בתמונת המערכת ולאלץ אותה לספק פרטי גרסה שתואמים לתמונת האתחול, אבל לא את הגרסה בפועל של המערכת. השילוב של אימות קובץ האימג' לאתחול, אימות dm-verity של תוכן קובץ האימג' של המערכת והעובדה ש-configure נקרא בשלב מוקדם מאוד בתהליך האתחול של המערכת אמורים להקשות על ניצול חלון ההזדמנויות הזה.