در مرحله بعد دو قطعه مهم از زبان مورد استفاده در Tink را تعریف می کنیم (به طور غیررسمی، اما سپس به طور رسمی)، یعنی Primitive و Interface .
اولیه
ابتدایی یک شی ریاضی است که مربوط به همه الگوریتم هایی است که برخی از کارها را به صورت ایمن انجام می دهند. برای مثال، الگوریتم اولیه AEAD شامل همه الگوریتمهای رمزگذاری است که ویژگیهای امنیتی مورد نیاز Tink از یک Aead را برآورده میکند.
ما تاکید می کنیم که اولیه ها به یک زبان برنامه نویسی یا روش خاصی برای دسترسی به آنها محدود نمی شوند. در عوض، باید به امر بدوی به عنوان یک شی کاملاً ریاضی فکر کرد. به عنوان مثال، اگر AEAD را در نظر بگیریم، اساساً از جفت توابع تشکیل می شود، یکی که رمزگذاری را انجام می دهد و دیگری که رمزگشایی را انجام می دهد.
رابط ها
رابط راهی است که در آن ما به کاربران دسترسی به یک اولیه را فراهم می کنیم. به عنوان مثال، ما انتظار داریم که در آینده Tink یک رابط Mac
و همچنین یک رابط StreamingMac
ارائه دهد که به شما امکان می دهد مک داده هایی را که مستقیماً در حافظه بارگذاری نمی شوند محاسبه کنید.
توجه داشته باشید که ما در اینجا به صراحت رابط ها و اولیه ها را تشخیص می دهیم. این باید روشن کند که شی ریاضی که این دو رابط به آن دسترسی می دهند یکسان هستند.
تعاریف رسمی
برای اکثر خوانندگان، توضیحات شهودی بالا احتمالا کافی است. با این وجود، ما احساس می کنیم که گاهی اوقات ارائه تعاریف رسمی از این مفاهیم می تواند مهم باشد.
توابع رمزنگاری
مفهوم تابع رمزنگاری به اندازه مفهوم اولیه مهم نیست، اما برای تعریف رسمی اولیه باید آن را معرفی کنیم.
- عملکرد رمزنگاری
یک تابع رمزنگاری یک نقشه است
\[ f: {\bf K} \times {\bf R} \times {\bf I} \to {\bf O}\]
از مجموعه \({\bf K}\) (فضای کلید)، مجموعه \({\bf R} = \{0,1\}^{\infty}\)(تصادفی، که فرض می کنیم مجموعه ای از رشته های بیتی بی نهایت است) و مجموعه \({\bf I}\) (فضای ورودی)، به مجموعه \({\bf O}\) (فضای خروجی).
بعداً مشخص خواهد شد که چرا یک پارامتر تصادفی خاص را اضافه کرده ایم.
به عنوان مثال، یک امکان را نشان میدهیم که چگونه میتوان این مفاهیم را به AES-GCM نگاشت. برای هر اندازه کلید معتبر \(s_k\)، اندازه nonce \(s_n\)، و اندازه برچسب\(s_t\)، AES-GCM از دو تابع رمزنگاری، یکی برای رمزگذاری و دیگری برای رمزگشایی تشکیل شده است. هر دو دارای یک فضای کلید \({\bf K} = \{0,1\}^{s_k}\)خواهند بود.
برای تابع رمزگذاری \(\mathrm{Enc}\)، اولین بیت های تصادفی \(s_n\) برای انتخاب nonce استفاده خواهد شد.
اجازه دهید \({\bf B} = \{0,1\}^8\) یک بایت را نشان دهد. فضای ورودی تابع رمزگذاری جفتهای \({\bf I} = {\bf B}^{*} \times {\bf B}^{*}\) از جفت رشتههای بایت با طول دلخواه است. اولین عنصر جفت پیام است، عنصر دوم داده های مرتبط است. استاندارد AES-GCM دارای محدودیت بالایی در طول ورودیها است، اما ما ترجیح میدهیم طولهای دلخواه را مجاز کنیم و در عوض یک نماد خطای خاص \(\bot\) به فضای خروجی اضافه کنیم. سپس فضای خروجی به \({\bf O} = {\bf B}^* \cup \{\bot\}\)تبدیل می شود، جایی که ما به طور دلخواه نتیجه محاسبات موفقیت آمیز را به عنوان الحاق \((\mathrm{IV} \| \mathrm{ciphertext} \| \mathrm{tag})\) همانطور که در استاندارد ارائه شده است، و خروجی\(\bot\)تعریف می کنیم، در صورتی که برخی از ورودی ها بیش از حد طولانی باشد. بنابراین، برای یک کلید ثابت، تابع رمزگذاری از نوع \(\mathrm{Enc}_k : {\bf R} \times {\bf B}^* \times {\bf B}^* \rightarrow {\bf B}^* \cup \{\bot\}\)می شود.
برای تابع رمزگشایی \(\mathrm{Dec}\) فضای کلید یکسان است. فضای ورودی به طور تصادفی یکسان است: \({\bf I} ={\bf B}^* \times {\bf B}^*\)، اما اکنون اولین عنصر خروجی تابع رمزگذاری است، در حالی که عنصر دوم هنوز داده های مرتبط است.
فضای خروجی نیز اتفاقاً همان \({\bf O} = {\bf B}^* \cup \{\bot\}\) است (دوباره یک تصادف). تفسیر تا حدودی متفاوت است، زیرا \(\bot\) معمولاً یک خطای احراز هویت را نشان می دهد (اگرچه در صورتی که برخی از ورودی ها بیش از حد طولانی باشد، این خروجی نیز خواهد بود).
ما تاکید می کنیم که رسمی سازی فوق تنها گزینه برای رسمی کردن استاندارد نیست . برای مثال، میتوان nonce را بخشی از ورودی در نظر گرفت، بهجای اینکه آن را از روی تصادفی بخوانیم (که منجر به یک بدوی بسیار متفاوت میشود). از طرف دیگر، میتوان خروجی را بهعنوان یک سهگانه حاوی nonce، متن رمزی و برچسب (بهجای الحاق) تعریف کرد. یا می توان فضای کلید را (تا حدودی خودسرانه) به\({\bf K} = \{0,1\}^{128} \cup \{0,1\}^{256}\)محدود کرد.
- الگوریتم رمزنگاری:
یک الگوریتم رمزنگاری (متقارن) یک تاپل است
\[(f_1, ... f_k)\]
از توابع رمزنگاری، که در آن همه توابع فضای کلید یکسانی دارند. نوع الگوریتم رمزنگاری تاپل \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\)است.
به عنوان مثال، برای هر \((s_k, s_n, s_t)\) سه گانه معتبر از اندازه کلید، nonce و برچسب، AES-GCM\({}_{s_k, s_n, s_t}\) یک الگوریتم رمزنگاری با دو تابع \(\mathrm{Enc}\) و \(\mathrm{Dec}\) است که در بالا توضیح داده شد.
ابتدایی ها و رابط ها
در مرحله بعد یک رمزنگاری اولیه را تعریف می کنیم.
- اولیه
- یک بدوی مجموعه ای از الگوریتم های رمزنگاری است که در آن همه الگوریتم ها دارای یک نوع \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\)هستند و فضاهای کلیدی الگوریتم ها به صورت جفتی جدا هستند.
به عنوان مثال، \(\mathrm{AEAD}\) اولیه را در Tink در نظر بگیرید. این الگوریتمهای متعددی دارد، از جمله AES-GCM برای اندازههای کلید 128 و 256 بیت، با اندازه غیرنسی 96 بیت، AES-EAX با برخی از اندازههای کلید و XChaCha20Poly1305. آنها فضاهای کلیدی مجزا دارند، اما همگی توابع رمزنگاری یکسان\(\mathrm{Enc}\) و \(\mathrm{Dec}\)را ارائه می دهند. (ما در این بحث رسمی هدفی را در فروپاشی اندازه های کلیدی مختلف AES-GCM نمی بینیم، اما مطمئناً می توان چنین کرد).
تعریف اصول اولیه
روش معمول تفکر اولیه این است که ابتدا ویژگیهای توابع رمزنگاری را تعریف کنیم و سپس به سادگی آن را به عنوان همه این الگوریتمها در نظر بگیریم.
برای مثال، برای AEAD میگوییم که \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a), a) = m\) «همیشه» راضی است (بهجز اگر متن ساده \(m\) خیلی طولانی باشد). علاوه بر این، ما ویژگی های امنیتی داریم. برای مثال، برای یک کلید تصادفی، رمزگذاری از نظر معنایی امن است.
سپس AEAD اولیه مجموعه ای از تمام الگوریتم های رمزنگاری است که این ویژگی ها را برآورده می کند. به عبارت دیگر، در عمل وقتی یک ابتدایی خاص را تعریف می کنیم، آن را بر اساس ویژگی ها تعریف می کنیم. همانطور که تعریف نشان می دهد، ما لیستی از الگوریتم ها را ارائه نمی دهیم.
رابط ها
یک رابط در Tink دسترسی به یک اولیه را می دهد، به این معنا که امکان محاسبه عنصری از فضای خروجی را از فضای ورودی فراهم می کند. به عنوان مثال، رابط AEAD در جاوا را در نظر بگیرید:
public interface Aead {
byte[] encrypt(byte[] plaintext, byte[] associated_data) throws GeneralSecurityException;
byte[] decrypt(byte[] ciphertext, byte[] associated_data) throws GeneralSecurityException;
}
توجه داشته باشید که ما به تصادفی بودن دسترسی نمی دهیم. در عوض، ما به کاربر اجازه می دهیم تا عناصر فضای ورودی را فراهم کند. عدم اجازه دسترسی به تصادفی بودن البته عمدی است. 1
Tink گاهی اوقات چندین رابط برای یک اولیه ارائه می دهد. این می تواند بسیار مفید باشد، زیرا الزامات گاهی اوقات متفاوت است. با این حال، انجام این کار هزینه دارد: به طور کلی، هر چه رابط های بیشتری ارائه دهد، قابلیت همکاری کمتری دارد. به عنوان مثال، تصور کنید که شخصی کتابخانه ای را بر اساس Tink می نویسد که از کاربر می خواهد یک شی Aead
را ارسال کند (برای رمزگذاری داخلی چیزی). اگر Tink اینترفیسهای بسیار زیادی را به \(\mathrm{AEAD}\) اولیه ارائه دهد، احتمال اینکه کاربر نمونهای آماده نداشته باشد که برای کلیدی که کاربر انتخاب کرده و کتابخانه را همزمان کار کند، زیاد است. از این رو، افزودن اینترفیس های بیشتر یک معامله است.
رمزهای AEAD این ویژگی را دارند که در برابر حملات متن رمزی انتخاب شده ایمن هستند، که تنها در صورت عدم استفاده مجدد از nonce تضمین می شود. رابط Aead در Tink به گونه ای طراحی شده است که از استفاده مجدد غیرمنتظره جلوگیری می کند: کاربر نمی تواند یک nonce را به عنوان ورودی برای رمزگذاری ارائه کند، در عوض، یک nonce جدید به طور تصادفی برای هر عملیات رمزگذاری ایجاد می شود. ↩