בדומה לרוב תוכנות ההצפנה של דיסקים וקבצים, הצפנת האחסון ב-Android מסתמכת בדרך כלל על מפתחות ההצפנה הגולמיים שקיימים בזיכרון המערכת, כדי שניתן יהיה לבצע את ההצפנה. גם כשההצפנה מתבצעת על ידי חומרה ייעודית ולא על ידי תוכנה, בדרך כלל התוכנה עדיין צריכה לנהל את מפתחות ההצפנה הגולמיים.
באופן מסורתי, זה לא נחשב לבעיה כי המפתחות לא קיימים במהלך מתקפה במצב אופליין, שהוא סוג המתקפה העיקרי שהצפנת האחסון נועדה להגן מפניו. עם זאת, יש רצון לספק הגנה משופרת מפני סוגים אחרים של מתקפות, כמו מתקפות אתחול קר ומתקפות אונליין שבהן התוקף יכול להדליף את זיכרון המערכת בלי לפגוע במכשיר באופן מלא.
כדי לפתור את הבעיה הזו, ב-Android 11 הוספנו תמיכה במפתחות שעטופים בחומרה, במקומות שבהם יש תמיכה בחומרה. מפתחות שעטופים בחומרה הם מפתחות אחסון שמוכרים בצורה גולמית רק לחומרה ייעודית. התוכנה רואה את המפתחות האלה ועובדת איתם רק בצורה עטופה (מוצפנת). החומרה הזו צריכה להיות מסוגלת ליצור ולייבא מפתחות אחסון, לעטוף מפתחות אחסון בטפסים זמניים וארוכי טווח, לגזור מפתחות משנה, לתכנת ישירות מפתח משנה אחד במנוע קריפטו מוטבע ולהחזיר מפתח משנה נפרד לתוכנה.
הערה: מנוע הצפנה מוטבע (או חומרה מוטבעת להצפנה) הוא חומרה שמצפינה או מפענחת נתונים בזמן שהם בדרך אל או מהתקן האחסון. בדרך כלל זהו בקר מארח UFS או eMMC שמטמיע את תוספי ההצפנה שמוגדרים במפרט JEDEC המתאים.
עיצוב
בקטע הזה מוצג העיצוב של התכונה 'מפתחות מוצפנים בחומרה', כולל תמיכה בחומרה שנדרשת בשביל התכונה. הדיון הזה מתמקד בהצפנה מבוססת-קבצים (FBE), אבל הפתרון רלוונטי גם להצפנת מטא-נתונים.
אחת הדרכים להימנע מהצורך במפתחות ההצפנה הגולמיים בזיכרון המערכת היא לשמור אותם רק במשבצות המפתחות של מנוע קריפטו מוטבע. עם זאת, לגישה הזו יש כמה בעיות:
- יכול להיות שמספר מפתחות ההצפנה גדול ממספר משבצות המפתחות.
- בדרך כלל, מנועי הצפנה מוטמעים מאבדים את התוכן של משבצות המפתחות שלהם אם בקר מארח האחסון מאופס. איפוס של בקר מארח האחסון הוא הליך סטנדרטי לשחזור שגיאות שמופעל אם מתרחשות שגיאות מסוימות באחסון, ושגיאות כאלה יכולות להתרחש בכל שלב. לכן, כשמשתמשים בהצפנה מוטמעת, מערכת ההפעלה צריכה להיות מוכנה תמיד לתכנת מחדש את משבצות המפתחות ללא התערבות של המשתמש.
- אפשר להשתמש במנועי הצפנה מוטבעים רק כדי להצפין או לפענח בלוקים מלאים של נתונים בדיסק. עם זאת, במקרה של FBE, התוכנה עדיין צריכה להיות מסוגלת לבצע עבודת הצפנה אחרת, כמו הצפנת שמות קבצים וקבלת מזהי מפתחות. התוכנה עדיין תצטרך גישה למפתחות FBE הגולמיים כדי לבצע את העבודה הזו.
כדי להימנע מהבעיות האלה, מפתחות האחסון הופכים למפתחות שעטופים בחומרה, שאפשר לפתוח את העטיפה שלהם ולהשתמש בהם רק באמצעות חומרה ייעודית. כך אפשר לתמוך במספר בלתי מוגבל של מפתחות. בנוסף, היררכיית המפתחות משתנה וחלק ממנה מועבר לחומרה הזו, מה שמאפשר להחזיר מפתח משני לתוכנה למשימות שלא יכולות להשתמש במנוע קריפטו מוטבע.
היררכיית מפתחות
אפשר לגזור מפתחות ממפתחות אחרים באמצעות פונקציה לגזירת מפתחות (KDF) כמו HKDF, וכך ליצור היררכיית מפתחות.
בתרשים הבא מוצגת היררכיית מפתחות אופיינית להצפנה מבוססת-קבצים (FBE) כשלא נעשה שימוש במפתחות שעטופים בחומרה:
מפתח המחלקה FBE הוא מפתח ההצפנה הגולמי ש-Android מעבירה לליבת Linux כדי לבטל את הנעילה של קבוצה מסוימת של ספריות מוצפנות, כמו האחסון המוצפן של פרטי הכניסה של משתמש Android מסוים. (בליבת המערכת, המפתח הזה נקרא מפתח מאסטר של fscrypt). ממפתח זה, ליבת המערכת גוזרת את מפתחות המשנה הבאים:
- מזהה המפתח. הערך הזה לא משמש להצפנה, אלא לזיהוי המפתח שבאמצעותו קובץ או ספרייה מסוימים מוגנים.
- המפתח להצפנת התוכן של הקובץ
- מפתח ההצפנה של שמות הקבצים
לעומת זאת, בתרשים הבא מוצגת היררכיית המפתחות של FBE כשמשתמשים במפתחות שעברו עטיפה בחומרה:
בהשוואה למקרה הקודם, נוספה רמה נוספת להיררכיית המפתחות, ומפתח ההצפנה של תוכן הקובץ הועבר. צומת הבסיס עדיין מייצג את המפתח שאנדרואיד מעבירה ל-Linux כדי לבטל את הנעילה של קבוצת ספריות מוצפנות. עם זאת, עכשיו המפתח נמצא בפורמט עטוף זמני, וכדי להשתמש בו צריך להעביר אותו לחומרה ייעודית. החומרה הזו צריכה להטמיע שני ממשקים שמקבלים מפתח שעטוף באופן זמני:
- ממשק אחד להפקת
inline_encryption_key
ולתכנות שלו ישירות במשבצת מפתחות במנוע ההצפנה המוטמע. כך אפשר להצפין או לפענח את התוכן של קובץ בלי שהתוכנה תקבל גישה למפתח הגולמי. בליבות המשותפות של Android, הממשק הזה תואם לפעולהblk_crypto_ll_ops::keyslot_program
, שצריכה להיות מוטמעת על ידי מנהל ההתקן של האחסון. - ממשק אחד שממנו אפשר לגזור ולהחזיר את
sw_secret
("software secret" – נקרא גם "raw secret" במקומות מסוימים), שהוא המפתח שמשמש את Linux לגזירת מפתחות המשנה לכל דבר מלבד הצפנת תוכן הקובץ. בליבות המשותפות של Android, הממשק הזה תואם לפעולהblk_crypto_ll_ops::derive_sw_secret
, שצריכה להיות מוטמעת על ידי מנהל ההתקן של האחסון.
כדי לגזור את inline_encryption_key
ואת sw_secret
ממפתח האחסון הגולמי, החומרה צריכה להשתמש ב-KDF חזק מבחינה קריפטוגרפית. ה-KDF הזה צריך לפעול לפי שיטות מומלצות להצפנה. הוא צריך להיות בעל חוזק אבטחה של לפחות 256 ביט, כלומר מספיק לכל אלגוריתם שישמש בהמשך. בנוסף, צריך להשתמש בתווית ובהקשר שונים כשמפיקים כל סוג של מפתח משנה, כדי להבטיח שמפתחות המשנה שיתקבלו יהיו מבודדים מבחינה קריפטוגרפית, כלומר, הידע על אחד מהם לא יחשוף אף אחד אחר. אין צורך בהרחבת מפתח, כי מפתח האחסון הגולמי הוא כבר מפתח אקראי אחיד.
מבחינה טכנית, אפשר להשתמש בכל KDF שעומד בדרישות האבטחה.
עם זאת, לצורכי בדיקה, vts_kernel_encryption_test
מטמיע את אותו KDF בתוכנה כדי לשחזר את הטקסט המוצפן בדיסק ולוודא שהוא נכון. כדי להקל על הבדיקה ולוודא שנעשה שימוש ב-KDF מאובטח שכבר נבדק, מומלץ להטמיע בחומרה את ה-KDF שמוגדר כברירת מחדל ושנבדק במהלך הבדיקה. אם משתמשים בחומרה עם פונקציית KDF שונה, אפשר לעיין במאמר בנושא בדיקת מפתחות מוצפנים כדי להבין איך להגדיר את הבדיקה בהתאם.
עטיפת מפתחות
כדי לעמוד ביעדי האבטחה של מפתחות שעטופים בחומרה, מוגדרים שני סוגים של עטיפת מפתחות:
- אריזה זמנית: החומרה מצפינה את המפתח הגולמי באמצעות מפתח שנוצר באופן אקראי בכל אתחול ולא נחשף ישירות מחוץ לחומרה.
- אריזה לטווח ארוך: החומרה מצפינה את המפתח הגולמי באמצעות מפתח ייחודי וקבוע שמוטמע בחומרה ולא נחשף ישירות מחוץ לחומרה.
כל המפתחות שמועברים לליבת Linux כדי לבטל את הנעילה של האחסון הם עטופים באופן זמני. כך, אם תוקף יצליח לחלץ מפתח שנמצא בשימוש מזיכרון המערכת, הוא לא יוכל להשתמש במפתח הזה לא רק מחוץ למכשיר, אלא גם במכשיר אחרי הפעלה מחדש.
במקביל, מערכת Android עדיין צריכה להיות מסוגלת לאחסן גרסה מוצפנת של המפתחות בדיסק, כדי שאפשר יהיה לבטל את הנעילה שלהם. המפתחות הגולמיים יתאימו למטרה הזו. עם זאת, מומלץ שהמפתחות הגולמיים לא יהיו בזיכרון המערכת בכלל, כדי שלא יהיה אפשר לחלץ אותם לשימוש מחוץ למכשיר, גם אם הם חולצו בזמן האתחול. לכן, מוגדר המושג של עטיפה לטווח ארוך.
כדי לתמוך בניהול מפתחות שעברו הצפנה בשתי הדרכים השונות האלה, החומרה צריכה להטמיע את הממשקים הבאים:
- ממשקי API ליצירה ולייבוא של מפתחות אחסון, שמוחזרים בצורה מוצפנת לטווח ארוך. הגישה לממשקי ה-API האלה היא עקיפה דרך KeyMint, והם תואמים לתג
TAG_STORAGE_KEY
KeyMint. היכולת 'יצירה' משמשת אתvold
ליצירת מפתחות אחסון חדשים לשימוש ב-Android, והיכולת 'ייבוא' משמשת אתvts_kernel_encryption_test
לייבוא מפתחות בדיקה. - ממשק להמרת מפתח אחסון עטוף לטווח ארוך למפתח אחסון עטוף לזמן קצר. הערך הזה תואם לשיטה
convertStorageKeyToEphemeral
KeyMint. השיטה הזו משמשת אתvold
ואתvts_kernel_encryption_test
כדי לבטל את הנעילה של האחסון.
אלגוריתם עטיפת המפתח הוא פרט הטמעה, אבל צריך להשתמש ב-AEAD חזק כמו AES-256-GCM עם IV אקראיים.
נדרשים שינויים בתוכנה
ל-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
) כי לא זוהתה תמיכה במפתחות שעטופים בחומרה. במקרה כזה, תוצאות הבדיקה עדיין יהיו 'עבר'.
כברירת מחדל, vts_kernel_encryption_test
מניח שהחומרה מיישמת KDF שהוא קורא לו kdf1
. ה-KDF הזה שייך למשפחת ה-KDF של מצב המונה מ-NIST SP 800-108, והוא משתמש ב-AES-256-CMAC כפונקציה פסאודו-אקראית. למידע נוסף על CMAC, אפשר לעיין במפרט של CMAC. הפונקציה KDF משתמשת בהקשרים ובתוויות ספציפיים כשמפיקים כל מפתח משנה. החומרה צריכה להטמיע את KDF, כולל הבחירה המדויקת של ההקשר, התווית והפורמט של מחרוזת הקלט הקבועה כשמפיקים כל מפתח משנה.
עם זאת, vts_kernel_encryption_test
מיישם גם פונקציות נוספות של KDF
kdf2
באמצעות kdf4
. הם מאובטחים באותה מידה כמו kdf1
, וההבדל היחיד הוא בבחירה של ההקשרים, התוויות והעיצוב של מחרוזת הקלט הקבועה. הם קיימים רק כדי להתאים את עצמם לחומרה שונה.
במכשירים שמשתמשים ב-KDF אחר, צריך להגדיר את מאפיין המערכת ro.crypto.hw_wrapped_keys.kdf
ב-PRODUCT_VENDOR_PROPERTIES
לשם ה-KDF כפי שמוגדר בקוד המקור של הבדיקה. כתוצאה מכך, vts_kernel_encryption_test
יחפש את KDF במקום kdf1
. לדוגמה, כדי לבחור את האפשרות
kdf2
, משתמשים בפקודה:
PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2
במכשירים שמשתמשים ב-KDF שהבדיקה לא תומכת בו, צריך גם להוסיף הטמעה של ה-KDF הזה לבדיקה ולתת לו שם ייחודי.
הפעלת מקשים עטופים
כשתמיכת המכשיר במפתח שעטוף בחומרה פועלת בצורה תקינה, מבצעים את השינויים הבאים בקובץ fstab
של המכשיר כדי שמערכת Android תשתמש בו להצפנת FBE ומטא-נתונים:
- FBE: מוסיפים את הדגל
wrappedkey_v0
לפרמטרfileencryption
. לדוגמה, משתמשים ב-fileencryption=::inlinecrypt_optimized+wrappedkey_v0
. פרטים נוספים זמינים במאמרי העזרה בנושא FBE. - הצפנת מטא-נתונים: מוסיפים את הדגל
wrappedkey_v0
לפרמטרmetadata_encryption
. לדוגמה, משתמשים ב-metadata_encryption=:wrappedkey_v0
. פרטים נוספים זמינים במסמכי התיעוד בנושא הצפנת מטא-נתונים.