הצפנת AES-GCM-HKDF של AEAD

המסמך הזה מגדיר באופן רשמי את הפונקציה המתמטית שמיוצגת על ידי מפתחות סטרימינג AES-GCM-HKDF, שמקודדים בפורמט פרוטו כ-type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey.

ההצפנה הזו מבוססת באופן חלש על סמך HRRV15 1. לניתוח האבטחה אנחנו קוראים את 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\}\).

בשלב הבא אנחנו משתמשים ב-HKDF 3 כפונקציית הגיבוב (hash) שמסופקת על ידי \(\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}$$ ולפענח כל קטע של מידע מוצפן (ciphertext) בנפרד.

ממשקי API עשויים (ובדרך כלל מאפשרים) לאפשר גישה אקראית או גישה לתחילת הקובץ בלי לבדוק את סוף הקובץ. זו פעולה מכוונת כי אפשר לפענח \(M_i\) מ- \(C_i\)בלי לפענח את כל הבלוקים של המידע המוצפן (ciphertext) הקודמים וגם שנותרו.

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

סריאליזציה וניתוח של מפתחות

כדי לבצע סריאליזציה של מפתח בפורמט Tink Proto, קודם אנחנו ממפים את הפרמטרים בצורה ברורה אל ה-Proto שנמסר ב-aes_gcm_hkdf_streaming.proto. צריך להגדיר את השדה version ל-0. לאחר מכן אנחנו מארגנים את המספר הזה באמצעות סריאליזציה רגילה ל-Proto, ומטמיעים את המחרוזת שמתקבלת בערך של השדה ב-KeyData. מגדירים את השדה type_url ל-type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey. לאחר מכן מגדירים את key_material_type ל-SYMMETRIC ומטמיעים אותו ב-Keyset. בדרך כלל אנחנו מגדירים את output_prefix_type ל-RAW. יוצא הדופן הוא שאם המפתח נותח עם ערך אחר שמוגדר ל-output_prefix_type, Tink יכול לכתוב RAW או את הערך הקודם.

כדי לנתח מפתח, אנחנו הופכים את התהליך שלמעלה (בדרך הרגילה בניתוח פרוטו). המערכת מתעלמת מהשדה key_material_type. אפשר להתעלם מהערך של output_prefix_type, או לדחות מפתחות שבהם output_prefix_type שונה מ-RAW. חובה לדחות מפתחות שיש להם version שונה מ-0.

בעיות מוכרות

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

קובצי עזר


  1. Hoang, Reyhanitabar, Rogaway, Vizar, 2015. הצפנה מאומתת אונליין והעמידות לשימוש חוזר לרעה. CRYPTO 2015. https://eprint.iacr.org/2015/189 

  2. הואנג, שן, 2020. האבטחה של הצפנה בסטרימינג בספריית Tink של Google. https://eprint.iacr.org/2020/1019 

  3. RFC 5869. פונקציית גזירה של מפתחות לחילוץ והרחבה של HMAC (HKDF). https://www.rfc-editor.org/rfc/rfc5869 

  4. NIST SP 800-38D. המלצה לחסימת מצבי פעולה להצפנה: Galois/Counter Mode (GCM) ו-GMAC. https://csrc.nist.gov/pubs/sp/800/38/d/final