Primitif dan Antarmuka

Selanjutnya, kami mendefinisikan (secara informal, tetapi lebih formal), dua bagian penting dari bahasa yang digunakan di Tink, yaitu Primitive dan Interface.

Primitif

Primitif adalah objek matematika yang sesuai dengan semua algoritma yang melakukan beberapa tugas dengan aman. Misalnya, primitif AEAD terdiri dari semua algoritma enkripsi yang memenuhi properti keamanan yang diperlukan Tink dari Aead.

Kami tekankan bahwa primitif tidak terikat pada bahasa pemrograman, atau cara khusus untuk mengaksesnya. Sebaliknya, kita harus menganggap primitif sebagai objek matematika saja. Misalnya, jika kami mempertimbangkan AEAD, pada dasarnya AEAD akan terdiri dari pasangan fungsi, satu yang melakukan enkripsi, dan satu yang melakukan dekripsi.

Antarmuka

Antarmuka adalah cara untuk menyediakan akses ke primitif kepada pengguna. Misalnya, kami berharap di masa mendatang, Tink akan menyediakan antarmuka Mac, tetapi juga antarmuka StreamingMac, yang memungkinkan untuk menghitung mac data yang tidak dimuat secara langsung ke dalam memori.

Perhatikan bahwa kita secara eksplisit membedakan antarmuka dan primitif di sini. Hal ini harus memperjelas bahwa objek matematika yang diberikan akses oleh kedua antarmuka ini sama.

Definisi formal

Bagi sebagian besar pembaca, penjelasan intuitif di atas mungkin cukup. Meskipun demikian, kami merasa bahwa terkadang penting untuk memberikan definisi formal konsep-konsep ini.

Fungsi kriptografis

Konsep fungsi kriptografi tidak sepenting konsep primitif, tetapi kita perlu memperkenalkannya untuk mendefinisikan secara formal.

Fungsi Kriptografi

Fungsi kriptografis adalah sebuah peta

\[ f: {\bf K} \times {\bf R} \times {\bf I} \to {\bf O}\]

dari set \({\bf K}\) (ruang kunci), set \({\bf R} = \{0,1\}^{\infty}\) (keacakan, yang kita asumsikan sebagai set bitstring tak terbatas), dan set \({\bf I}\) (ruang input), ke set \({\bf O}\) (ruang output).

Akan menjadi jelas mengapa kita menambahkan parameter pengacakan tertentu.

Sebagai contoh, kami menunjukkan satu kemungkinan cara pemetaan konsep ini ke AES-GCM. Untuk setiap ukuran kunci \(s_k\), ukuran nonce \(s_n\), dan ukuran tag\(s_t\)yang valid, AES-GCM terdiri dari dua fungsi kriptografi, satu untuk enkripsi, dan satu untuk dekripsi. Keduanya akan memiliki spasi kunci yang sama \({\bf K} = \{0,1\}^{s_k}\).

Untuk fungsi enkripsi \(\mathrm{Enc}\), bit \(s_n\) pertama keacakan akan digunakan untuk memilih nonce.

Mari \({\bf B} = \{0,1\}^8\) menunjukkan byte. Ruang input fungsi enkripsi adalah pasangan \({\bf I} = {\bf B}^{*} \times {\bf B}^{*}\) pasang string byte dengan panjang arbitrer. Elemen pertama pada pasangan dimaksudkan sebagai pesan, elemen kedua adalah data terkait. Standar AES-GCM memiliki batas atas panjang input, tetapi kami memilih untuk mengizinkan panjang arbitrer, dan sebagai gantinya menambahkan simbol error khusus \(\bot\) ke ruang output. Ruang output kemudian menjadi \({\bf O} = {\bf B}^* \cup \{\bot\}\), tempat kami secara bebas menentukan hasil komputasi yang berhasil sebagai penyambungan \((\mathrm{IV} \| \mathrm{ciphertext} \| \mathrm{tag})\) seperti yang diberikan dalam standar, dan output \(\bot\), jika beberapa input terlalu panjang. Oleh karena itu, untuk kunci tetap, fungsi enkripsi menjadi jenis \(\mathrm{Enc}_k : {\bf R} \times {\bf B}^* \times {\bf B}^* \rightarrow {\bf B}^* \cup \{\bot\}\).

Untuk fungsi dekripsi \(\mathrm{Dec}\) ruang kuncinya sama. Ruang input secara kebetulan sama: \({\bf I} ={\bf B}^* \times {\bf B}^*\), tetapi kini elemen pertama dimaksudkan sebagai output fungsi enkripsi, sedangkan elemen kedua masih merupakan data terkait.

Ruang output juga sama \({\bf O} = {\bf B}^* \cup \{\bot\}\) (sekali lagi kebetulan). Penafsirannya agak berbeda, karena \(\bot\) biasanya menunjukkan error autentikasi (meskipun itu juga akan menjadi output jika beberapa input terlalu panjang).

Kami menekankan bahwa formalisasi di atas bukan satu-satunya pilihan untuk memformalkan standar. Misalnya, orang dapat menganggap nonce sebagai bagian dari input, bukan membacanya dari keacakan (yang menghasilkan primitif yang sangat berbeda). Atau, seseorang dapat menentukan output sebagai triple yang berisi nonce, ciphertext, dan tag (bukan penyambungan). Atau, seseorang dapat membatasi ruang kunci (agak acak) ke \({\bf K} = \{0,1\}^{128} \cup \{0,1\}^{256}\).

Algoritma Kriptografi:

Algoritma kriptografis (simetris) adalah tuple

\[(f_1, ... f_k)\]

fungsi kriptografi, di mana semua fungsi memiliki ruang kunci yang sama. Jenis algoritma kriptografis adalah tuple \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\).

Misalnya, untuk setiap tiga \((s_k, s_n, s_t)\) ukuran kunci, nonce, dan tag yang valid, AES-GCM\({}_{s_k, s_n, s_t}\) adalah algoritma kriptografi dengan dua fungsi \(\mathrm{Enc}\) dan \(\mathrm{Dec}\) dijelaskan di atas.

Primitif dan antarmuka

Selanjutnya kami mendefinisikan primitif kriptografi.

Primitif
primitif adalah kumpulan algoritma kriptografi, dengan semua algoritme memiliki jenis yang sama \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\), dan ruang kunci algoritma terpisah berpasangan.

Sebagai contoh, pertimbangkan \(\mathrm{AEAD}\) primitif di Tink. Platform ini memiliki beberapa algoritma, di antaranya adalah AES-GCM untuk ukuran kunci 128 dan 256 bit, dengan ukuran nonce 96 bit, AES-EAX dengan beberapa ukuran kunci, dan XChaCha20Poly1305. Keduanya memiliki ruang kunci yang terpisah, tetapi semuanya memberikan fungsi kriptografis \(\mathrm{Enc}\) dan \(\mathrm{Dec}\)yang sama. (Kami tidak melihat tujuannya entah bagaimana cara menciutkan berbagai ukuran kunci AES-GCM dalam diskusi formal ini, tetapi tentu saja kita bisa melakukannya).

Menentukan primitif

Cara umum untuk menganggap primitif adalah terlebih dahulu menentukan properti fungsi kriptografi, lalu hanya menganggap primitif sebagai algoritma tersebut.

Misalnya, untuk AEAD, kami akan mengatakan bahwa \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a), a) = m\) 'selalu' puas (kecuali, misalnya jika teks biasa \(m\) terlalu panjang). Selain itu, kami memiliki properti keamanan; misalnya, untuk kunci acak, enkripsinya sangat aman.

Primitif AEAD selanjutnya adalah kumpulan semua algoritma kriptografi yang memenuhi properti ini. Dengan kata lain, dalam praktiknya, saat kita mendefinisikan primitif tertentu, kita mendefinisikannya berdasarkan properti. Kami tidak memberikan daftar algoritma, seperti yang disarankan oleh definisi.

Antarmuka

Antarmuka di Tink memberikan akses ke primitif, dalam artian memungkinkan untuk menghitung elemen ruang output dari ruang input. Misalnya, pertimbangkan antarmuka AEAD di Java:

public interface Aead {
  byte[] encrypt(byte[] plaintext, byte[] associated_data) throws GeneralSecurityException;
  byte[] decrypt(byte[] ciphertext, byte[] associated_data) throws GeneralSecurityException;
}

Harap perhatikan bahwa kami tidak memberikan akses ke jenis acak tersebut. Sebagai gantinya, kita memungkinkan pengguna menyediakan elemen ruang input. Melarang akses ke pengacakan tentu saja bertujuan.1

Tink terkadang menawarkan beberapa antarmuka untuk satu primitif. Hal ini bisa sangat berguna, karena persyaratan terkadang berbeda. Namun, tindakan ini memiliki konsekuensi: secara umum, semakin banyak antarmuka yang ditawarkan, semakin rendah interoperabilitasnya. Misalnya, bayangkan seseorang menulis library berdasarkan Tink yang mengharuskan pengguna meneruskan objek Aead (untuk mengenkripsi sesuatu secara internal). Jika Tink menawarkan terlalu banyak antarmuka yang berbeda untuk \(\mathrm{AEAD}\) primitif, kemungkinan besar pengguna tidak memiliki instance yang siap digunakan untuk kunci yang dipilih pengguna dan library secara bersamaan. Oleh karena itu, menambahkan lebih banyak antarmuka adalah sebuah {i>trade-off<i}.


  1. Cipher AEAD memiliki properti yang aman dari serangan ciphertext yang dipilih, yang dijamin hanya jika nonce tidak digunakan kembali. Antarmuka Aead di Tink dirancang sedemikian rupa sehingga mencegah penggunaan ulang nonce: pengguna tidak dapat memberikan nonce sebagai input untuk enkripsi. Sebagai gantinya, nonce baru dibuat secara acak untuk setiap operasi enkripsi.