זרימת AESD של AES-CTR HMAC

במסמך הזה מוגדר באופן רשמי הפונקציה המתמטית שמיוצגת על ידי מפתחות ה-Streaming של HMAC-CTR‏ (מקודדים בפורמט proto בתור type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey).

ההצפנה הזו מבוססת באופן רופף על [HRRV15]1. לניתוח של האבטחה, אפשר לעיין במאמר [HS20]2. שימו לב גם שבבדיקות של Tink בשפות שונות יש בדיקה בשם aes_ctr_hmac_streaming_key_test.py שמכילה את test_manually_created_test_vector עם הוראות מפורטות לקבלת טקסט מוצפן.

מפתח ופרמטרים

המפתחות מתוארים באמצעות החלקים הבאים (כל הגדלים במסמך הזה הם בייטים):

  • \(\mathrm{InitialKeyMaterial}\), מחרוזת בייטים: חומר המפתח הראשוני.
  • \(\mathrm{CiphertextSegmentSize} \in \{1, 2, \ldots, 2^{31}-1\}\).
  • \(\mathrm{DerivedKeySize} \in \{16, 32\}\).
  • \(\mathrm{HkdfHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256}, \mathrm{SHA512}\}\).
  • \(\mathrm{HmacHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256}, \mathrm{SHA512}\}\).
  • \(\mathrm{HmacTagSize} \in \mathbb{N}\).

מפתחות תקינים צריכים לעמוד גם בתנאים הבאים:

  • \(\mathrm{len}(\mathrm{InitialKeyMaterial}) \geq \mathrm{DerivedKeySize}\).
  • If \(\mathrm{HmacHashType} = \mathrm{SHA1}\) then \(\mathrm{HmacTagSize} \in \{10, \ldots, 20\}\).
  • If \(\mathrm{HmacHashType} = \mathrm{SHA256}\) then \(\mathrm{HmacTagSize} \in \{10, \ldots, 32\}\).
  • If \(\mathrm{HmacHashType} = \mathrm{SHA512}\) then \(\mathrm{HmacTagSize} \in \{10, \ldots, 64\}\).
  • \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + \mathrm{HmacTagSize} + 8\) (הערך הזה שווה ל-\(\mathrm{len}(\mathrm{Header}) + \mathrm{HmacTagSize}\) , כפי שמוסבר בהמשך).

מפתחות שלא עומדים באף אחד מהמאפיינים האלה נדחים על ידי 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{DerivedKeySize} + 32\) עבור ההודעה הזו:\(k := \mathrm{HKDF}(\mathrm{InitialKeyMaterial}, \mathrm{Salt}, \mathrm{AssociatedData})\). הקלטים משמשים בערכים המתאימים של\(\mathrm{HKDF}\): \(\mathrm{InitialKeyMaterial}\) הוא \(\mathrm{ikm}\),\(\mathrm{Salt}\) הוא המלח ו-\(\mathrm{AssociatedData}\) משמש כ- \(\mathrm{info}\).

לאחר מכן, המחרוזת \(k\) מופרדת לשני חלקים \(k_1 \| k_2 := k\), כך ש-\(\mathrm{len}(k_1) = \mathrm{DerivedKeySize}\) ו- \(\mathrm{len}(k_2) = 32\).

פיצול ההודעה

לאחר מכן, ההודעה \(M\) מופרדת לחלקים: \(M = M_0 \| M_1 \| \cdots \| M_{n-1}\).

האורך שלהם נבחר כך שיתאימו לתנאים הבאים:

  • \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{HmacTagSize}\}\).
  • אם \(n > 1\), אז \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{HmacTagSize}\}\).
  • אם \(n > 1\), אז \(M_{0}, \ldots, M_{n-2}\) חייב להיות באורך המקסימלי בהתאם למגבלות שלמעלה.

בחלוקה הזו,הערך של \(n\) יכול להיות לכל היותר \(2^{32}\). אחרת, ההצפנה נכשלת.

הצפנת הבלוק

כדי להצפין את הקטע \(M_i\), קודם מחשבים את\(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b \| 0x00000000\), שבו מקודדים את \(\mathrm{i}\) ב-4 בייטים באמצעות קידוד big-endian, ומגדירים את הבייט $b$ כ-0x00 אם $i < n-1$, וכ-0x01 במקרים אחרים.

לאחר מכן אנחנו מצפינים \(M_i\) באמצעות מפתח AES CTR \(k_1\)וווקטור אתחול\(\mathrm{IV}_i\). במילים אחרות, הקלטות להפעלות של AES הן\(\mathrm{IV}_i, \mathrm{IV}_i + 1, \mathrm{IV}_i + 2, \ldots\), כאשר \(\mathrm{IV}_i\) מתפרש כמספר שלם ב-big-endian. התוצאה היא \(C'_i\).

אנחנו מחשבים את התג באמצעות HMAC עם פונקציית הגיבוב (hash) שמקבלים מ- \(\mathrm{HmacHashType}\) ועם המפתח \(k_2\) על פני שרשור\(\mathrm{IV}_i \| C'_i\).

לאחר מכן אנחנו משרשרים את הטקסט המוצפן ואחריו את התג כדי לקבל את \(C_i\).

שרשור הפלחים

בסוף, כל הפלחים מחוברים יחד כ-\(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\), שהוא הטקסט המוצפן הסופי.

פונקציית פענוח

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

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

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

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

כדי לסדר מפתח בסריאליזציה בפורמט Tink Proto, קודם כול ממפים את הפרמטרים באופן הברור ל-proto שמופיע ב-aes_ctr_hmac_streaming.proto. צריך להגדיר את השדה version לערך 0. לאחר מכן אנחנו מבצעים סריאליזציה באמצעות סריאליזציית proto רגילה, ומטמיעים את המחרוזת שנוצרת בשדה הערך של פרוטו של KeyData. מגדירים את השדה type_url לערך type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey. לאחר מכן מגדירים את 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 יידחו.

קובצי עזר


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

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

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