יישום dm-verity

אנדרואיד 4.4 ואילך תומך באתחול מאומת באמצעות תכונת ליבת ה-device-mapper-verity (dm-verity), המספקת בדיקת תקינות שקופה של התקני חסימה. dm-verity עוזר למנוע rootkits מתמשכים שיכולים להחזיק בהרשאות שורש ולפגוע במכשירים. תכונה זו עוזרת למשתמשי אנדרואיד להיות בטוחים בעת אתחול מכשיר הוא נמצא באותו מצב שבו נעשה בו שימוש אחרון.

יישומים פוטנציאליים מזיקים (PHA) עם הרשאות בסיס יכולים להסתתר מתוכניות זיהוי ולהסוות את עצמם אחרת. תוכנת ההשרשה יכולה לעשות זאת מכיוון שלעתים קרובות היא מוסמכת יותר מהגלאים, מה שמאפשר לתוכנה "לשקר" לתוכניות האיתור.

תכונת dm-verity מאפשרת לך להסתכל על התקן בלוק, שכבת האחסון הבסיסית של מערכת הקבצים, ולקבוע אם הוא תואם את התצורה הצפויה שלו. הוא עושה זאת באמצעות עץ גיבוב קריפטוגרפי. עבור כל בלוק (בדרך כלל 4k), יש Hash SHA256.

מכיוון שערכי ה-hash מאוחסנים בעץ של דפים, יש לסמוך רק על ה-hash ה"שורש" ברמה העליונה כדי לאמת את שאר העץ. היכולת לשנות כל אחד מהבלוקים תהיה שווה ערך לשבירת ה-hash ההצפנה. ראה את התרשים הבא לתיאור של מבנה זה.

dm-verity-hash-table

איור 1. טבלת hash dm-verity

מפתח ציבורי כלול במחיצת האתחול, אשר חייב להיות מאומת חיצונית על ידי יצרן המכשיר. מפתח זה משמש כדי לאמת את החתימה עבור אותו hash ולאשר שמחיצת המערכת של המכשיר מוגנת וללא שינוי.

מבצע

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

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

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

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

אם האימות נכשל, ההתקן יוצר שגיאת קלט/פלט המציינת שלא ניתן לקרוא את הבלוק. זה ייראה כאילו מערכת הקבצים פגומה, כצפוי.

יישומים עשויים לבחור להמשיך ללא הנתונים המתקבלים, למשל כאשר תוצאות אלו אינן נדרשות לתפקוד הראשי של היישום. עם זאת, אם האפליקציה לא יכולה להמשיך ללא הנתונים, היא תיכשל.

תיקון שגיאות קדימה

אנדרואיד 7.0 ואילך משפר את חוסן ה-dm-verity עם תיקון שגיאות קדימה (FEC). הטמעת AOSP מתחילה עם קוד תיקון השגיאות הנפוץ של Reed-Solomon ומחילה טכניקה הנקראת שזירה כדי לצמצם תקורה של שטח ולהגדיל את מספר הבלוקים הפגומים שניתן לשחזר. לפרטים נוספים על FEC, ראה אתחול מאומת באכיפה קפדנית עם תיקון שגיאות .

יישום

סיכום

  1. צור תמונת מערכת ext4.
  2. צור עץ חשיש עבור התמונה הזו.
  3. בנה טבלת dm-verity עבור עץ הגיבוב הזה.
  4. חתום על טבלת ה-dm-verity כדי לייצר חתימת טבלה.
  5. חבר את חתימת הטבלה ואת טבלת dm-verity למטא-נתונים של verity.
  6. שרשור את תמונת המערכת, מטא-נתוני האמת ועץ הגיבוב.

ראה את The Chromium Projects - Verified Boot לתיאור מפורט של עץ הגיבוב וטבלת dm-verity.

יצירת עץ האש

כפי שמתואר בהקדמה, עץ הגיבוב הוא חלק בלתי נפרד מ-dm-verity. הכלי cryptsetup יפיק עבורך עץ hash. לחלופין, תואם מוגדר כאן:

<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>

כדי ליצור את ה-hash, תמונת המערכת מפוצלת בשכבה 0 ל-4k בלוקים, שלכל אחד מהם מוקצה hash SHA256. שכבה 1 נוצרת על ידי הצטרפות רק לאותם Hash SHA256 לתוך בלוקים של 4k, וכתוצאה מכך תמונה קטנה בהרבה. שכבה 2 נוצרת באופן זהה, עם הגיבובים SHA256 של שכבה 1.

זה נעשה עד ש-Hashs SHA256 של השכבה הקודמת יכולים להתאים לבלוק בודד. כשמקבלים את ה-SHA256 של הבלוק הזה, יש לך את ה-hash השורש של העץ.

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

אם יש לך בלוק בשכבה שאינה מלאה באופן טבעי על ידי הגיבובים של השכבה הקודמת, עליך לרפד אותו באפסים כדי להשיג את ה-4k הצפוי. זה מאפשר לך לדעת שעץ הגיבוב לא הוסר ובמקום זאת הושלם עם נתונים ריקים.

כדי ליצור את עץ הגיבוב, שרשרת את ה-hash של שכבה 2 לאלה של שכבה 1, שכבה 3 את ה-hash לאלה של שכבה 2, וכן הלאה. כתוב את כל זה לדיסק. שים לב שזה לא מתייחס לשכבה 0 של ה-hash השורש.

לסיכום, האלגוריתם הכללי לבניית עץ הגיבוב הוא כדלקמן:

  1. בחר מלח אקראי (קידוד הקסדצימלי).
  2. שחרר את תמונת המערכת שלך לבלוקים של 4k.
  3. עבור כל בלוק, קבל Hash (מלוח) שלו SHA256.
  4. שרשור הגיבובים האלה כדי ליצור רמה
  5. רפד את הרמה עם 0s עד גבול בלוק של 4k.
  6. שרשור את הרמה לעץ האש שלך.
  7. חזור על שלבים 2-6 תוך שימוש ברמה הקודמת כמקור לשלב הבא עד שיהיה לך רק hash בודד.

התוצאה של זה היא hash בודד, שהוא hash השורש שלך. זה והמלח שלך משמשים במהלך בניית טבלת המיפוי dm-verity שלך.

בניית טבלת המיפוי dm-verity

בנה את טבלת המיפוי dm-verity, המזהה את התקן החסימה (או היעד) עבור הליבה ואת המיקום של עץ הגיבוב (שהוא אותו ערך). מיפוי זה משמש ליצירת fstab ואתחול. הטבלה גם מזהה את גודל הבלוקים ואת ה-hash_start, מיקום ההתחלה של עץ ה-hash (באופן ספציפי, מספר הבלוק שלו מתחילת התמונה).

ראה cryptsetup לתיאור מפורט של שדות טבלת מיפוי יעדי האמת.

חתימה על טבלת dm-verity

חתום על טבלת dm-verity כדי לייצר חתימת טבלה. בעת אימות מחיצה, חתימת הטבלה מאומתת תחילה. זה נעשה מול מפתח בתמונת האתחול שלך במיקום קבוע. מפתחות כלולים בדרך כלל במערכות הבנייה של היצרנים להכללה אוטומטית במכשירים במיקום קבוע.

כדי לאמת את המחיצה באמצעות חתימה ושילוב מקשים זה:

  1. הוסף מפתח RSA-2048 בפורמט תואם libmincrypt למחיצת /boot ב- /verity_key . זהה את המיקום של המפתח המשמש לאימות עץ הגיבוב.
  2. ב-fstab עבור הערך הרלוונטי, הוסף verify לדגלי fs_mgr .

אגירה של חתימת הטבלה למטא נתונים

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

כבדיקת שפיות, מספר קסם משויך לכל סט של מטא נתונים של טבלה שעוזר לזהות את הטבלה. מכיוון שהאורך כלול בכותרת תמונת מערכת ext4, זה מספק דרך לחפש את המטא נתונים מבלי לדעת את תוכן הנתונים עצמם.

זה מוודא שלא בחרת לאמת מחיצה לא מאומתת. אם כן, היעדר מספר קסם זה יעצור את תהליך האימות. מספר זה דומה ל:
0xb001b001

ערכי הבתים ב-hex הם:

  • בייט ראשון = b0
  • בייט שני = 01
  • בייט שלישי = b0
  • בייט רביעי = 01

התרשים הבא מתאר את הפירוט של מטא-נתוני האמת:

<magic number>|<version>|<signature>|<table length>|<table>|<padding>
\-------------------------------------------------------------------/
\----------------------------------------------------------/   |
                            |                                  |
                            |                                 32K
                       block content

והטבלה הזו מתארת ​​את שדות המטא נתונים האלה.

טבלה 1. שדות מטא-נתונים של Verity

שדה מַטָרָה גודל ערך
מספר קסם משמש את fs_mgr כבדיקת שפיות 4 בתים 0xb001b001
גִרְסָה משמש לגרסה של בלוק המטא נתונים 4 בתים כרגע 0
חֲתִימָה החתימה של הטבלה בצורה מרופדת PKCS1.5 256 בתים
אורך השולחן אורך טבלת dm-verity בבתים 4 בתים
שולחן טבלת dm-verity שתוארה קודם לכן בתים באורך טבלה
ריפוד מבנה זה מרופד ב-0 עד 32k באורך 0

אופטימיזציה של dm-verity

כדי להפיק את הביצועים הטובים ביותר מ-dm-verity, עליך:

  • בקרנל, הפעל את NEON SHA-2 עבור ARMv7 ואת הרחבות SHA-2 עבור ARMv8.
  • נסה עם הגדרות שונות לקריאה קדימה ו-prefetch_cluster כדי למצוא את התצורה הטובה ביותר עבור המכשיר שלך.