בדומה לרוב תוכנות ההצפנה של דיסקים וקבצים, הצפנת האחסון של Android מסתמכת באופן מסורתי על כך שמפתחות ההצפנה הגולמיים נמצאים בזיכרון המערכת, כדי שניתן יהיה לבצע את ההצפנה. גם כשהצפנה מתבצעת באמצעות חומרה ייעודית ולא באמצעות תוכנה, בדרך כלל עדיין צריך לנהל את מפתחות ההצפנה הגולמיים באמצעות תוכנה.
בדרך כלל, הבעיה הזו לא נחשבת בעייתית כי המפתחות לא נמצאים במהלך התקפה אופליין, שהיא סוג ההתקפה העיקרי שהצפנת האחסון נועדה להגן מפניו. עם זאת, יש רצון לספק הגנה מוגברת מפני סוגים אחרים של התקפות, כמו התקפות הפעלה קרה והתקפות אונליין שבהן תוקף עשוי לדלוף את זיכרון המערכת בלי לפגוע במכשיר באופן מלא.
כדי לפתור את הבעיה הזו, הוספנו ל-Android 11 תמיכה במפתחות שמוגדרים בחומרה, במכשירים שיש בהם תמיכה בחומרה. מפתחות שמוסתרים בחומרה הם מפתחות אחסון שידועים רק בחומרה ייעודית בצורה גולמית. התוכנה רואה את המפתחות האלה רק בצורה ארוזה (מוצפנת) ופועלת איתם רק בצורה כזו. החומרה הזו צריכה להיות מסוגלת ליצור ולייבא מפתחות אחסון, לעטוף מפתחות אחסון בצורות זמניות וארוכות טווח, להפיק מפתחות משנה, לתכנת מפתח משנה אחד ישירות במנוע הצפנה מוטמע ולהחזיר מפתח משנה נפרד לתוכנה.
הערה: מנוע הצפנה מוטמע (או חומרה להצפנה מוטמעת) הוא חומרה שמצפינה או מפענחת נתונים בזמן שהם בדרך אל מכשיר האחסון או ממנו. בדרך כלל זהו בקר מארח של UFS או eMMC שמטמיע את התוספים הקריפטוגרפיים שמוגדרים במפרט JEDEC התואם.
עיצוב
בקטע הזה נסביר על העיצוב של התכונה 'מפתחות שמוגדרים בחומרה', כולל התמיכה הנדרשת בחומרה. הדיון הזה מתמקד בהצפנה מבוססת-קבצים (FBE), אבל הפתרון רלוונטי גם להצפנת מטא-נתונים.
אחת מהדרכים להימנע מהצורך במפתחות ההצפנה הגולמיים בזיכרון המערכת היא לשמור אותם רק ב-keyslots של מנוע הצפנה מוטמע. עם זאת, לגישה הזו יש כמה בעיות:
- מספר מפתחות ההצפנה עשוי להיות גדול ממספר חריצי המפתחות.
- בדרך כלל, מנועי הצפנה מוטמעים מאבדים את התוכן של חריצי המפתחות שלהם אם בקר האחסון (בדרך כלל UFS או eMMC) מאופס. איפוס של בקר האחסון הוא תהליך סטנדרטי לשחזור שגיאות, שמתבצע אם מתרחשות שגיאות אחסון מסוגים מסוימים. שגיאות כאלה יכולות להתרחש בכל שלב. לכן, כשמשתמשים בהצפנה מוטמעת, מערכת ההפעלה תמיד צריכה להיות מוכנה לתכנת מחדש את חריצי המפתחות ללא התערבות של המשתמש.
- אפשר להשתמש במנועי הצפנה מוטמעים רק כדי להצפין או לפענח בלוקים מלאים של נתונים בדיסק. עם זאת, במקרה של FBE, התוכנה עדיין צריכה להיות מסוגלת לבצע משימות קריפטוגרפיות אחרות, כמו הצפנת שמות קבצים וביצוע הפעלה של מזהים של מפתחות. לתוכנה עדיין תהיה צורך בגישה למפתחות ה-FBE הגולמיים כדי לבצע את הפעולות האחרות האלה.
כדי למנוע את הבעיות האלה, מפתחות האחסון הופכים למפתחות עטופים בחומרה, שרק חומרה ייעודית יכולה לפתוח ולהשתמש בהם. כך אפשר לתמוך במספר בלתי מוגבל של מפתחות. בנוסף, היררכיית המפתחות משתנה ומועברת חלקית לחומרה הזו, וכך מאפשרת להחזיר מפתח משנה לתוכנה למשימות שלא ניתן להשתמש בהן במנוע קריפטוגרפיה מוטמע.
היררכיית מפתחות
אפשר להפיק מפתחות ממפתחות אחרים באמצעות KDF (פונקציית הפקה של מפתחות) כמו HKDF, וכתוצאה מכך נוצרת היררכיית מפתחות.
בתרשים הבא מוצגת היררכיית מפתחות אופיינית ל-FBE כשלא נעשה שימוש במפתחות שמוסתרים בחומרה:
מפתח הכיתה של FBE הוא מפתח ההצפנה הגולמי שמערכת Android מעבירה לליבה של Linux כדי לפתוח קבוצה מסוימת של ספריות מוצפנות, כמו האחסון המוצפן של פרטי הכניסה של משתמש מסוים ב-Android. (במעבד, המפתח הזה נקרא מפתח מאסטר של fscrypt). מהמפתח הזה, הליבה מפיחה את מפתחות המשנה הבאים:
- מזהה המפתח. הוא לא משמש להצפנה, אלא הוא ערך שמזהה את המפתח שמשמש להגנה על קובץ או על ספרייה מסוימים.
- מפתח ההצפנה של תוכן הקובץ
- מפתח ההצפנה של שמות הקבצים
לעומת זאת, בתרשים הבא מוצגת היררכיית המפתחות של FBE כשמשתמשים במפתחות שמוגדרים בחומרה:
בהשוואה למקרה הקודם, נוספה רמה נוספת להיררכיית המפתחות ומפתח ההצפנה של תוכן הקובץ הועבר למיקום אחר. צומת הבסיס עדיין מייצג את המפתח ש-Android מעבירה ל-Linux כדי לבטל את הנעילה של קבוצת ספריות מוצפנות. עם זאת, עכשיו המפתח נמצא באריזת זמן קצר, ולכן כדי להשתמש בו צריך להעביר אותו לחומרה ייעודית. החומרה הזו צריכה ליישם שני ממשקים שמקבלים מפתח ארעי:
- ממשק אחד להפיכת
inline_encryption_key
לקוד ולתכנותו ישירות ב-keyslot של מנוע הקריפטו המוטמע. כך אפשר להצפין או לפענח את תוכן הקבצים בלי שתוכנה תקבל גישה למפתח הגולמי. בליבות הנפוצות של 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 in Counter Mode" עם AES-256-CMAC כ-PRF. חשוב לזכור: כדי שהמפתחות יהיו תואמים, כל החלקים של האלגוריתם צריכים להיות זהים, כולל הבחירה של ההקשרים והתוויות של KDF לכל מפתח משנה.
אריזה של מפתחות
כדי לעמוד ביעדים של אבטחת מפתחות באריזת חומרה, מוגדרים שני סוגים של אריזת מפתחות:
- אריזה זמנית: החומרה מצפינה את המפתח הגולמי באמצעות מפתח שנוצר באופן אקראי בכל הפעלה ולא נחשף ישירות מחוץ לחומרה.
- אריזה לטווח ארוך: החומרה מצפינה את המפתח הגולמי באמצעות מפתח ייחודי וקבוע שמוטמע בחומרה ולא נחשף ישירות מחוץ לחומרה.
כל המפתחות המועברים לליבת 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
, וגם את התמיכה בליבה של 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
. פרטים נוספים זמינים במסמכי העזרה בנושא הצפנת המטא-נתונים.