במסמך הזה מוגדר באופן רשמי הפונקציה המתמטית שמיוצגת על ידי מפתחות סטרימינג של AES-GCM-HKDF, שמקודדים בפורמט proto בתור type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey
.
ההצפנה הזו מבוססת באופן רופף על HRRV151. לניתוח אבטחה, אפשר לעיין במאמר HS202.
מפתח ופרמטרים
המפתחות מתוארים באמצעות החלקים הבאים (כל הגדלים במסמך הזה הם בייטים):
- \(\mathrm{KeyValue}\), מחרוזת בייטים.
- \(\mathrm{CiphertextSegmentSize} \in \{1, 2, \ldots, 2^{31}-1\}\).
- \(\mathrm{DerivedKeySize} \in \{16, 32\}\).
- \(\mathrm{HkdfHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256}, \mathrm{SHA512}\}\).
מפתחות תקינים צריכים לעמוד גם בתנאים הבאים:
- \(\mathrm{len}(\mathrm{KeyValue}) \geq \mathrm{DerivedKeySize}\).
- \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + 24\) (הערך הזה שווה ל- \(\mathrm{len}(\mathrm{Header}) + 16\) , כפי שמוסבר בהמשך).
מפתחות שלא עומדים באף אחד מהמאפיינים האלה נדחים על ידי Tink, בין שבזמן הניתוח של המפתח ובין שבזמן יצירת הפרימיטיב התואם.
פונקציית הצפנה
כדי להצפין הודעה \(\mathrm{Msg}\) עם נתונים משויכים\(\mathrm{AssociatedData}\), אנחנו יוצרים כותרת, מפצלים את ההודעה לפלחים, מצפינים כל פלח ומקשרים את הפלחים המוצפנים.
יצירת הכותרת
בוחרים מחרוזת אקראית אחידה \(\mathrm{Salt}\) באורך \(\mathrm{DerivedKeySize}\) ומחרוזת אקראית אחידה \(\mathrm{NoncePrefix}\) באורך 7.
לאחר מכן מגדירים את \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\), כאשר אורך הכותרת מקודד כבאיט אחד. חשוב לזכור \(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\).
לאחר מכן, אנחנו משתמשים ב-HKDF3 עם פונקציית הגיבוב שניתנת על ידי \(\mathrm{HkdfHashType}\) ופונקציות הקלט \(\mathrm{ikm} := \mathrm{KeyValue}\), \(\mathrm{salt} := \mathrm{Salt}\)ו- \(\mathrm{info} := \mathrm{AssociatedData}\), עם אורך הפלט \(\mathrm{DerivedKeySize}\). אנחנו קוראים לתוצאה \(\mathrm{DerivedKey}\).
פיצול ההודעה
לאחר מכן, ההודעה \(\mathrm{Msg}\) מופרדת לחלקים: \(\mathrm{Msg} = M_0 \| M_1 \| \cdots \| M_{n-1}\).
האורך שלהם נבחר כך שיתאימו לתנאים הבאים:
- \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{16}\}\).
- אם \(n>1\), אז \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{16}\}\).
- אם \(n>1\), האורך המקסימלי של \(\mathrm{len}(M_{0}), \ldots, \mathrm{len}(M_{n-2})\) חייב להיות בהתאם למגבלות שלמעלה.
הערך של\(n\) יכול להיות לכל היותר \(2^{32}\). אחרת, ההצפנה נכשלת.
הצפנת הבלוק
כדי להצפין את הקטע \(M_i\), מחשבים את הערך \(\mathrm{IV}_i := \mathrm{NoncePrefix}
\| \mathrm{i} \| b\), כאשר \(\mathrm{i}\) הוא 4 בייטים בקידוד big-endian, והבייט b הוא 0x00
אם i < n-1, ו-0x01
במקרים אחרים.
לאחר מכן אנחנו מצפינים את \(M_i\) באמצעות AES-GCM4, כאשר המפתח הוא\(\mathrm{DerivedKey}\), וקטור האתחול הוא \(\mathrm{IV}_i\), והנתונים המשויכים הם המחרוזת הריקה. \(C_i\) הוא התוצאה של ההצפנה הזו (כלומר, שרשור של \(C\) ו- \(T\) בקטע 5.2.1.2 במסמך העזרה המקושר בנושא AES-GCM).
שרשור הפלחים המוצפנים
בסיום, כל הפלחים מחוברים זה לזה כ- \(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\), שהוא הטקסט המוצפן הסופי.
פענוח
הפענוח הופך את ההצפנה. אנחנו משתמשים בכותרת כדי לקבל את\(\mathrm{NoncePrefix}\), ומפענחים כל מקטע של טקסט מוצפן בנפרד.
ממשקי API עשויים לאפשר גישה אקראית (ובדרך כלל מאפשרים זאת), או גישה לתחילת הקובץ בלי לבדוק את סוף הקובץ. הסיבה לכך היא שאפשר לפענח את \(M_i\) מ- \(C_i\)בלי לפענח את כל הבלוקים הקודמים והנותרים של הטקסט המוצפן.
עם זאת, צריך להיזהר בממשקי API כדי למנוע מהמשתמשים להתבלבל בין שגיאות של סוף קובץ לשגיאות של פענוח: סביר להניח שבשני המקרים ה-API יצטרך להחזיר שגיאה, והתעלמות מההבדל עלולה לאפשר ליריב לקצר קבצים באופן יעיל.
סריאליזציה ופירוק של מפתחות
כדי לסדר מפתח בסריאליזציה בפורמט Tink Proto, קודם כול ממפים את הפרמטרים באופן הברור ל-proto שמופיע ב-aes_gcm_hkdf_streaming.proto. צריך להגדיר את השדה version
לערך 0. לאחר מכן אנחנו מבצעים סריאליזציה של המחרוזת הזו באמצעות סריאליזציה רגילה של proto, ומטמיעים את המחרוזת שנוצרה בשדה הערך של proto של KeyData. מגדירים את השדה type_url
לערך type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey
. לאחר מכן מגדירים את key_material_type
כ-SYMMETRIC
ומטמיעים אותו בקבוצת מפתחות. בדרך כלל אנחנו מגדירים את output_prefix_type
כ-RAW
. היוצא מן הכלל הוא שאם המפתח נותח עם ערך שונה שהוגדר ל-output_prefix_type
, Tink עשויה לכתוב את RAW
או את הערך הקודם.
כדי לנתח מפתח, אנחנו מבצעים את התהליך שלמעלה בכיוון ההפוך (בדרך הרגילה לניתוחים של protos). המערכת מתעלמת מהשדה key_material_type
. אפשר להתעלם מהערך של output_prefix_type
, או לדחות מפתחות עם output_prefix_type
שונה מ-RAW
. צריך לדחות מפתחות עם הערך version
ששונה מ-0.
בעיות מוכרות
לא צפוי שאפשר יהיה להשתמש בהטמעות של פונקציית ההצפנה שלמעלה ב-fork. בטיחות ביצירת פורקים
קובצי עזר
-
Hoang, Reyhanitabar, Rogaway, Vizar, 2015. הצפנה מאומתת אונליין והתנגדות לניצול לרעה של שימוש חוזר במזהה חד-פעמי. CRYPTO 2015. https://eprint.iacr.org/2015/189 ↩
-
Hoang, Shen, 2020. אבטחה של הצפנת סטרימינג בספריית Tink של Google. https://eprint.iacr.org/2020/1019 ↩
-
RFC 5869. פונקציית הפקת מפתחות (HKDF) המבוססת על HMAC. https://www.rfc-editor.org/rfc/rfc5869 ↩
-
NIST SP 800-38D. המלצה למודלים של פעולה של הצפנת בלוקים: Galois/Counter Mode (GCM) ו-GMAC. https://csrc.nist.gov/pubs/sp/800/38/d/final ↩