AES-CTR एचएमएसी स्ट्रीमिंग AEAD

इस दस्तावेज़ में, एईएस-सीटीआर एचएमएससी स्ट्रीमिंग पासकोड (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}\).
  • अगर \(\mathrm{HmacHashType} = \mathrm{SHA1}\) तो \(\mathrm{HmacTagSize} \in \{10, \ldots, 20\}\).
  • अगर \(\mathrm{HmacHashType} = \mathrm{SHA256}\) तो \(\mathrm{HmacTagSize} \in \{10, \ldots, 32\}\).
  • अगर \(\mathrm{HmacHashType} = \mathrm{SHA512}\) तो \(\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}\) सात हो.

इसके बाद, हम\(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\)को सेट करते हैं, जहां हेडर की लंबाई को एक बाइट के तौर पर एन्कोड किया जाता है. हमें पता चला है कि \(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\).

इसके बाद, हम इस मैसेज के लिए,\(k := \mathrm{HKDF}(\mathrm{InitialKeyMaterial}, \mathrm{Salt}, \mathrm{AssociatedData})\)\(\mathrm{DerivedKeySize} + 32\) की लंबाई वाले पासकोड का हिसाब लगाने के लिए, \(\mathrm{HkdfHashType}\)के साथ HKDF3 का इस्तेमाल करते हैं. इनपुट का इस्तेमाल,\(\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}\) को चार बाइट में एन्कोड करते हैं. साथ ही, अगर $i < n-1$ है, तो बाइट $b$ को 0x00 पर सेट करते हैं और अगर ऐसा नहीं है, तो 0x01 पर सेट करते हैं.

इसके बाद, हम \(M_i\) एईएस सीटीआर पासकोड \(k_1\)और इनिशलाइज़ेशन वेक्टर\(\mathrm{IV}_i\)का इस्तेमाल करके, डेटा को एन्क्रिप्ट करते हैं. दूसरे शब्दों में, AES के इनवोकेशन के इनपुट\(\mathrm{IV}_i, \mathrm{IV}_i + 1, \mathrm{IV}_i + 2, \ldots\)हैं, जहां \(\mathrm{IV}_i\) को बिग-एंडियन इंटिजर के तौर पर समझा जाता है. इससे \(C'_i\)मिलता है.

हम एचएमएसी का इस्तेमाल करके टैग का हिसाब लगाते हैं. इसके लिए, \(\mathrm{HmacHashType}\) के दिए गए हैश फ़ंक्शन और\(\mathrm{IV}_i \| C'_i\)को जोड़ने के बाद \(k_2\) की कुंजी का इस्तेमाल किया जाता है.

इसके बाद, हम \(C_i\)पाने के लिए, सिफरटेक्स्ट को टैग के साथ जोड़ते हैं.

सेगमेंट को जोड़ना

आखिर में, सभी सेगमेंट को\(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\)के तौर पर जोड़ दिया जाता है. यह फ़ाइनल सिफरटेक्स्ट होता है.

डिक्रिप्शन फ़ंक्शन

डिक्रिप्ट करने का मतलब है, एन्क्रिप्ट (सुरक्षित) किए गए डेटा को वापस उसके मूल स्वरूप में लाना. हम नॉन्स पाने के लिए हेडर का इस्तेमाल करते हैं और सिफरटेक्स्ट के हर सेगमेंट को अलग-अलग डिक्रिप्ट करते हैं.

एपीआई, फ़ाइल के आखिर में मौजूद डेटा की जांच किए बिना, फ़ाइल के शुरू में मौजूद डेटा को ऐक्सेस कर सकते हैं. आम तौर पर, एपीआई ऐसा करते हैं. ऐसा इसलिए किया जाता है, क्योंकि \(C_i\)से \(M_i\) को डिक्रिप्ट किया जा सकता है. इसके लिए, पिछले और बाकी सभी सिफरटेक्स्ट ब्लॉक को डिक्रिप्ट करने की ज़रूरत नहीं होती.

हालांकि, एपीआई को इस बात का ध्यान रखना चाहिए कि उपयोगकर्ता, फ़ाइल के आखिर में पहुंचने और डिक्रिप्ट करने से जुड़ी गड़बड़ियों को एक-दूसरे से न जोड़ें: दोनों ही मामलों में, एपीआई को गड़बड़ी का मैसेज दिखाना पड़ सकता है. साथ ही, इस अंतर को अनदेखा करने पर, कोई भी व्यक्ति फ़ाइलों को काट सकता है.

कुंजियों को सीरियलाइज़ करना और पार्स करना

किसी कुंजी को "Tink Proto" फ़ॉर्मैट में सीरियलाइज़ करने के लिए, हम सबसे पहले पैरामीटर को aes_ctr_hmac_streaming.proto में दिए गए प्रोटो में साफ़ तौर पर मैप करते हैं. version फ़ील्ड को 0 पर सेट करना होगा. इसके बाद, हम सामान्य प्रोटो सीरियलाइज़ेशन का इस्तेमाल करके इसे सीरियलाइज़ करते हैं. साथ ही, इससे बनी स्ट्रिंग को KeyData प्रोटो के फ़ील्ड की वैल्यू में एम्बेड करते हैं. हमने type_url फ़ील्ड को type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey पर सेट किया है. इसके बाद, हम key_material_type को SYMMETRIC पर सेट करते हैं और इसे कीसेट में जोड़ देते हैं. आम तौर पर, हम output_prefix_type को RAW पर सेट करते हैं. हालांकि, अगर पासकोड को output_prefix_type के लिए सेट की गई किसी दूसरी वैल्यू के साथ पार्स किया गया था, तो Tink RAW या पिछली वैल्यू लिख सकता है.

किसी पासकोड को पार्स करने के लिए, हम ऊपर बताई गई प्रोसेस को उलटा करते हैं. प्रोटोकोड को पार्स करते समय, आम तौर पर यही तरीका अपनाया जाता है. key_material_type फ़ील्ड को अनदेखा कर दिया जाता है. output_prefix_type की वैल्यू को अनदेखा किया जा सकता है या RAW से अलग output_prefix_type वाली कुंजियों को अस्वीकार किया जा सकता है. जिन कुंजियों में version की वैल्यू 0 से अलग है उन्हें अस्वीकार कर दिया जाएगा.

रेफ़रंस


  1. [HRRV15] Hoang, Reyhanitabar, Rogaway, Vizar. ऑनलाइन पुष्टि वाला एन्क्रिप्शन और नॉन्स का फिर से इस्तेमाल करने से जुड़ी गलत गतिविधियों से बचाव. CRYPTO 2015. https://eprint.iacr.org/2015/189 

  2. [HS20] Google की Tink लाइब्रेरी में स्ट्रीमिंग एन्क्रिप्शन की सुरक्षा. Hoang, Shen, 2020. https://eprint.iacr.org/2020/1019 

  3. [HKDF] एचएमएसी पर आधारित पासकोड निकालने और उसे बड़ा करने का फ़ंक्शन (HKDF), आरएफ़सी 5869. https://www.rfc-editor.org/rfc/rfc5869