מפתחות עטופים בחומרה

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

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

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

הערה: מנוע הצפנה מוטבע (או מנוע מוטמע) חומרה להצפנה) מתייחסת לחומרה שמצפינה/מפענחת נתונים בזמן הוא בדרך אל התקן האחסון או ממנו. בדרך כלל זהו בקר מארח של UFS או eMMC שמטמיע את התוספים הקריפטוגרפיים שמוגדרים במפרט JEDEC התואם.

עיצוב

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

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

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

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

היררכיית מפתחות

אפשר להפיק מפתחות ממפתחות אחרים באמצעות KDF (פונקציית הפקה של מפתחות) כמו HKDF, וכתוצאה מכך נוצרת היררכיית מפתחות.

בתרשים הבא מוצגת היררכיית מפתחות אופיינית ל-FBE כשלא נעשה שימוש במפתחות שמוסתרים בחומרה:

היררכיית מפתחות FBE (רגילה)
איור 1. היררכיית מפתחות של FBE (רגילה)

מפתח הכיתה של FBE הוא מפתח ההצפנה הגולמי שמערכת Android מעבירה לליבה של Linux כדי לפתוח קבוצה מסוימת של ספריות מוצפנות, כמו האחסון המוצפן של פרטי הכניסה של משתמש מסוים ב-Android. (במעבד, המפתח הזה נקרא מפתח מאסטר של fscrypt). מהמפתח הזה, הליבה מפיחה את מפתחות המשנה הבאים:

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

לעומת זאת, בתרשים הבא מוצגת היררכיית המפתחות של FBE כשמשתמשים במפתחות שמוגדרים בחומרה:

היררכיית מפתחות FBE (עם מפתח עטוף חומרה)
איור 2. היררכיית מפתחות FBE (עם מפתח עטוף חומרה)

בהשוואה למקרה הקודם, נוספה רמה נוספת להיררכיית המפתחות ומפתח ההצפנה של תוכן הקובץ הועבר למיקום אחר. הרמה הבסיסית (root) צומת עדיין מייצג את המפתח ש-Android מעביר ל-Linux כדי לבטל נעילה של של הספריות המוצפנות. אבל עכשיו המפתח הזה עטוף באופן זמני, כדי להשתמש בו, יש להעביר אותו לחומרה ייעודית. החומרה הזו צריכה ליישם שני ממשקים שמקבלים מפתח ארעי:

  • ממשק אחד להפקת inline_encryption_key וישירות לתכנת אותו לחריץ מפתח של מנוע ההצפנה המוטבע. כך אפשר להעלות קובץ של התוכן, כך שיוצפן/מפוענח בלי שלתוכנה תהיה גישה לנתונים הגולמיים מקש. בליבות הנפוצות של Android, הממשק הזה תואם הפעולה blk_crypto_ll_ops::keyslot_program, שחייבת להיות הוטמע על ידי מנהל התקן האחסון.
  • ממשק אחד להפיכה ולהחזרת sw_secret ('סוד תוכנה' – שנקרא גם 'סוד גולמי' במקומות מסוימים), שהוא המפתח ש-Linux משתמש בו כדי להפיק את מפתחות המשנה לכל דבר מלבד הצפנת תוכן הקובץ. בליבות הנפוצות של Android, הממשק הזה תואם לפעולה blk_crypto_ll_ops::derive_sw_secret, שחייבת להיות מוטמעת על ידי מנהל האחסון.

כדי להפיק את inline_encryption_key ו-sw_secret מתוך מפתח אחסון גולמי, החומרה חייבת להשתמש ב-KDF חזק מבחינה קריפטוגרפית. ה-KDF חייב לפעול בהתאם לשיטות המומלצות לקריפטוגרפיה, וחוזק האבטחה שלו צריך להיות לפחות 256 ביט, כלומר מספיק לכל אלגוריתם שמיושם בשלב מאוחר יותר. בנוסף, צריך להשתמש בתווית, בהקשר ובמחרוזת מידע ייחודית לאפליקציה כשמפיקים כל סוג של מפתח משנה, כדי להבטיח שהמפתחות המשניים שמתקבלים יהיו מבודדים מבחינה קריפטוגרפית, כלומר, הידע על אחד מהם לא יגלה מידע על מפתח אחר. אין צורך בהקשחת מפתחות, כי מפתח האחסון הגולמי כבר הוא מפתח אקראי אחיד.

מבחינה טכנית, אפשר להשתמש בכל KDF שעומד בדרישות האבטחה. אבל למטרות בדיקה, צריך להטמיע מחדש את אותו ערך KDF לבדיקת הקוד. נכון לעכשיו, KDF אחד נבדק ויושם. אפשר למצוא אותו בקוד המקור עבור vts_kernel_encryption_test. מומלץ שהחומרה תשתמש ב-KDF הזה, שמשתמש ב-NIST SP 800-108 "KDF במצב מונה" עם AES-256-CMAC כ-PRF. לתשומת ליבכם: כדי להבטיח תאימות, חלקים באלגוריתם חייבים להיות זהים, כולל הבחירה בהקשרים של KDF ואת התוויות לכל מפתח משנה.

עטיפת מפתחות

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

  • אריזה זמנית: החומרה מצפינה את המפתח הגולמי באמצעות מפתח שנוצר באופן אקראי בכל הפעלה ולא חשוף ישירות מחוץ לחומרה.
  • אריזה לטווח ארוך: החומרה מצפינה את המפתח הגולמי באמצעות מפתח ייחודי וקבוע שמוטמע בחומרה ולא נחשף ישירות מחוץ לחומרה.

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

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

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

  • ממשקים ליצירה ולייבוא של מפתחות אחסון, והחזרתם באריזת לטווח ארוך. הגישה לממשקים האלה מתבצעת באופן עקיף דרך KeyMint, והם תואמים לתג KeyMint‏ TAG_STORAGE_KEY. היכולת 'יצירה' משמשת את vold ליצירת מפתחות אחסון חדשים לשימוש ב-Android, והיכולת 'ייבוא' משמשת את vts_kernel_encryption_test לייבוא מפתחות בדיקה.
  • ממשק להמרה של מפתח אחסון ארוז לטווח ארוך מפתח אחסון עטוף זמנית. תואמת convertStorageKeyToEphemeral שיטת KeyMint. השיטה הזו משמשת גם את vold וגם את vts_kernel_encryption_test כדי לבטל את הנעילה של האחסון.

אלגוריתם האריזה של המפתח הוא פרט בהטמעה, אבל צריך להשתמש בו ב-AEAD חזק כמו AES-256-GCM עם IVs אקראיים.

צריך לבצע שינויים בתוכנה

כבר יש ב-AOSP מסגרת בסיסית לתמיכה במפתחות שמוגדרים בחומרה. הזה כוללת את התמיכה ברכיבי מרחב משתמשים כמו vold, וגם כתמיכה בליבה (kernel) של Linux ב-blk-crypto, ב-fscrypt וב- dm-default-key.

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

שינויים ב-KeyMint

צריך לשנות את ההטמעה של KeyMint במכשיר כך שתתמוך ב-TAG_STORAGE_KEY ותטמיע את השיטה convertStorageKeyToEphemeral.

ב-Keymaster, נעשה שימוש ב-exportKey במקום ב-convertStorageKeyToEphemeral.

שינויים בליבה של Linux

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

בליבות android14 ואילך, מגדירים את BLK_CRYPTO_KEY_TYPE_HW_WRAPPED ב-blk_crypto_profile::key_types_supported, מוסיפים ל-blk_crypto_ll_ops::keyslot_program ול-blk_crypto_ll_ops::keyslot_evict תמיכה בתכנות או בהוצאה של מפתחות עטופים בחומרה, ומטמיעים את blk_crypto_ll_ops::derive_sw_secret.

לליבות android12 ו-android13, הגדרה של BLK_CRYPTO_FEATURE_WRAPPED_KEYS בעוד blk_keyslot_manager::features, ליצור blk_ksm_ll_ops::keyslot_program ו-blk_ksm_ll_ops::keyslot_evict תמיכה בתכנות/בפינוי מפתחות עטופים בחומרה, ולהטמיע את blk_ksm_ll_ops::derive_raw_secret.

בליבות android11, מגדירים את BLK_CRYPTO_FEATURE_WRAPPED_KEYS ב-keyslot_manager::features, מוסיפים ל-keyslot_mgmt_ll_ops::keyslot_program ול-keyslot_mgmt_ll_ops::keyslot_evict תמיכה בתכנות או בהוצאה של מפתחות עטופים בחומרה, ומטמיעים את keyslot_mgmt_ll_ops::derive_raw_secret.

בדיקה

קשה יותר לבדוק הצפנה עם מפתחות עטופים בחומרה מאשר הצפנה באמצעות מפתחות רגילים, עדיין אפשר לבצע בדיקה על ידי ייבוא מפתח בדיקה להטמיע מחדש את נגזרת המפתחות שהחומרה עושה. ההטמעה מתבצעת ב-vts_kernel_encryption_test. כדי להריץ את הבדיקה הזו, מריצים את הפקודה:

atest -v vts_kernel_encryption_test

קוראים את יומן הבדיקה ומוודאים שתרחישי הבדיקה של מפתחות שמוגדרים בחומרה (לדוגמה, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy ו-DmDefaultKeyTest.TestHwWrappedKey) לא הושמטו בגלל שלא זוהתה תמיכה במפתחות שמוגדרים בחומרה, כי תוצאות הבדיקה עדיין יהיו 'עברו' במקרה כזה.

הפעלת מקשים

אחרי שתראו שהתמיכה במפתחות שמוגדרים בחומרה פועלת כמו שצריך, תוכלו לבצע את השינויים הבאים בקובץ fstab של המכשיר כדי לאפשר ל-Android להשתמש בו ל-FBE ולצפנת מטא-נתונים:

  • FBE: מוסיפים את הדגל wrappedkey_v0 לפרמטר fileencryption. לדוגמה, השתמשו fileencryption=::inlinecrypt_optimized+wrappedkey_v0 למידע נוסף, עיינו במסמכי העזרה של FBE.
  • הצפנת מטא-נתונים: מוסיפים את הדגל wrappedkey_v0 לפרמטר metadata_encryption. לדוגמה, השתמשו metadata_encryption=:wrappedkey_v0 פרטים נוספים זמינים במסמכי העזרה בנושא הצפנת המטא-נתונים.