این سند به طور رسمی تابع ریاضی نشاندادهشده توسط کلیدهای جریانی AES-GCM-HKDF را تعریف میکند که در قالب پروتو بهعنوان type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey
کدگذاری شدهاند.
این رمزگذاری بر اساس HRRV15 1 است. برای تجزیه و تحلیل امنیتی، به HS20 2 مراجعه می کنیم.
کلید و پارامترها
کلیدها با قسمت های زیر توصیف می شوند (همه اندازه های این سند بر حسب بایت هستند):
- \(\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 با تابع هش داده شده توسط استفاده می کنیم \(\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}\) در رمزگذاری big-endian 4 بایت است و بایت $b$ 0x00
است اگر $i < n-1$ و در غیر این صورت 0x01
.
سپس رمزگذاری می کنیم \(M_i\) با استفاده از AES-GCM 4 ، که در آن کلید است\(\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"، ابتدا پارامترها را به روشی واضح در پروتوی ارائه شده در aes_gcm_hkdf_streaming.proto نگاشت می کنیم. version
فیلد باید روی 0 تنظیم شود. سپس این را با استفاده از سریالسازی پروتو معمولی سریال میکنیم و رشته حاصل را در مقدار فیلد یک پروتوی KeyData قرار میدهیم. فیلد type_url
را روی type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey
تنظیم کردیم. سپس key_material_type
روی SYMMETRIC
قرار می دهیم و آن را در یک مجموعه کلید قرار می دهیم. ما معمولا output_prefix_type
را روی RAW
قرار می دهیم. استثنا این است که اگر کلید با مقدار متفاوتی برای output_prefix_type
تجزیه شود، Tink ممکن است RAW
یا مقدار قبلی را بنویسد.
برای تجزیه یک کلید، فرآیند فوق را معکوس می کنیم (به روش معمول هنگام تجزیه پروتوها). فیلد key_material_type
نادیده گرفته می شود. مقدار output_prefix_type
را می توان نادیده گرفت یا کلیدهایی را که output_prefix_type
متفاوت از RAW
دارند رد کرد. کلیدهایی که دارای version
متفاوت از 0 هستند باید رد شوند.
مسائل شناخته شده
انتظار نمی رود که پیاده سازی های تابع رمزگذاری فوق امن باشد. به ایمنی چنگال مراجعه کنید.
مراجع
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 ↩