ابتدایی ها و رابط ها

در مرحله بعد دو قطعه مهم از زبان مورد استفاده در 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\)، اندازه غیر عادی \(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}\) ابتدایی در تینک این الگوریتم‌های متعددی دارد، از جمله 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 را ارسال کند (برای رمزگذاری داخلی چیزی). اگر تینک اینترفیس های بسیار متفاوتی را به آن ارائه دهد \(\mathrm{AEAD}\) بدوی، احتمال اینکه کاربر نمونه ای آماده نداشته باشد که برای کلیدی که کاربر انتخاب کرده و کتابخانه به طور همزمان کار کند زیاد است. از این رو، افزودن اینترفیس های بیشتر یک معامله است.


  1. رمزهای AEAD این ویژگی را دارند که در برابر حملات متن رمزی انتخاب شده ایمن هستند، که تنها در صورت عدم استفاده مجدد از nonce تضمین می شود. رابط Aead در Tink به گونه ای طراحی شده است که از استفاده مجدد غیرمنتظره جلوگیری می کند: کاربر نمی تواند یک nonce را به عنوان ورودی برای رمزگذاری ارائه کند، در عوض، یک nonce جدید به طور تصادفی برای هر عملیات رمزگذاری ایجاد می شود.