Dokumen ini secara resmi menentukan fungsi matematika yang diwakili oleh kunci Streaming HMAC AES-CTR (dienkode dalam format proto sebagai type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey
).
Enkripsi ini secara longgar didasarkan pada [HRRV15]1. Untuk analisis keamanan, kami merujuk ke [HS20]2. Perhatikan juga bahwa pengujian lintas bahasa Tink
memiliki pengujian
aes_ctr_hmac_streaming_key_test.py yang
berisi test_manually_created_test_vector
dengan panduan lengkap tentang cara
mendapatkan ciphertext.
Kunci dan parameter
Kunci dijelaskan menurut bagian-bagian berikut (semua ukuran dalam dokumen ini dalam byte):
- \(\mathrm{InitialKeyMaterial}\), string byte: materi kunci awal.
- \(\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}\).
Kunci yang valid juga memenuhi properti berikut:
- \(\mathrm{len}(\mathrm{InitialKeyMaterial}) \geq \mathrm{DerivedKeySize}\).
- Jika \(\mathrm{HmacHashType} = \mathrm{SHA1}\) maka \(\mathrm{HmacTagSize} \in \{10, \ldots, 20\}\).
- Jika \(\mathrm{HmacHashType} = \mathrm{SHA256}\) maka \(\mathrm{HmacTagSize} \in \{10, \ldots, 32\}\).
- Jika \(\mathrm{HmacHashType} = \mathrm{SHA512}\) maka \(\mathrm{HmacTagSize} \in \{10, \ldots, 64\}\).
- \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + \mathrm{HmacTagSize} + 8\) (Ini sama dengan \(\mathrm{len}(\mathrm{Header}) + \mathrm{HmacTagSize}\) seperti yang akan dijelaskan nanti).
Kunci yang tidak memenuhi salah satu properti ini akan ditolak oleh Tink (baik saat kunci diurai atau saat primitif yang sesuai dibuat).
Fungsi enkripsi
Untuk mengenkripsi pesan \(\mathrm{Msg}\) dengan data terkait \(\mathrm{AssociatedData}\), kami membuat header, membagi pesan menjadi beberapa segmen, mengenkripsi setiap segmen, dan menyambungkan segmen. Kami akan menjelaskan langkah-langkah ini sebagai berikut.
Membuat header
Untuk membuat header, pertama-tama pilih string acak yang seragam \(\mathrm{Salt}\) dengan panjang \(\mathrm{DerivedKeySize}\). Selanjutnya, kita memilih string acak yang seragam \(\mathrm{NoncePrefix}\) dengan panjang 7.
Kemudian, kami menetapkan \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\), dengan panjang header dienkode sebagai satu byte. Kami mendapati bahwa \(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\).
Selanjutnya, kita menggunakan HKDF3 dengan fungsi hash \(\mathrm{HkdfHashType}\) untuk menghitung materi kunci dengan panjang \(\mathrm{DerivedKeySize} + 32\) untuk pesan ini: \(k := \mathrm{HKDF}(\mathrm{InitialKeyMaterial}, \mathrm{Salt}, \mathrm{AssociatedData})\). Input digunakan dalam input masing-masing \(\mathrm{HKDF}\): \(\mathrm{InitialKeyMaterial}\) adalah \(\mathrm{ikm}\), \(\mathrm{Salt}\) adalah salt, dan \(\mathrm{AssociatedData}\) digunakan sebagai \(\mathrm{info}\).
String \(k\) kemudian dibagi menjadi dua bagian \(k_1 \| k_2 := k\), sehingga \(\mathrm{len}(k_1) = \mathrm{DerivedKeySize}\) dan \(\mathrm{len}(k_2) = 32\).
Memisahkan pesan
Pesan \(M\) berikutnya akan dibagi menjadi beberapa bagian: \(M = M_0 \| M_1 \| \cdots \| M_{n-1}\).
Panjangnya dipilih agar memenuhi:
- \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{HmacTagSize}\}\).
- Jika \(n > 1\), maka \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{HmacTagSize}\}\).
- Jika \(n > 1\), \(M_{0}, \ldots, M_{n-2}\) harus memiliki panjang maksimal sesuai dengan batasan di atas.
Dalam pembagian ini, \(n\) maksimal mungkin \(2^{32}\). Jika tidak, enkripsi akan gagal.
Mengenkripsi blok
Untuk mengenkripsi segmen \(M_i\), pertama-tama kami menghitung
\(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b \| 0x00000000\),
tempat kami mengenkode \(\mathrm{i}\) dalam 4
byte menggunakan encoding big-endian, dan menetapkan byte $b$ menjadi 0x00
jika $i < n-1$
dan 0x01
sebaliknya.
Kemudian, kami mengenkripsi \(M_i\) menggunakan kunci CTR AES \(k_1\), dan vektor inisialisasi \(\mathrm{IV}_i\). Dengan kata lain, input ke pemanggilan AES adalah\(\mathrm{IV}_i, \mathrm{IV}_i + 1, \mathrm{IV}_i + 2, \ldots\)dengan \(\mathrm{IV}_i\) diinterpretasikan sebagai bilangan bulat big-endian. Tindakan ini akan menghasilkan \(C'_i\).
Kami menghitung tag menggunakan HMAC dengan fungsi hash yang diberikan oleh \(\mathrm{HmacHashType}\) dan dengan kunci \(k_2\) pada penyambungan \(\mathrm{IV}_i \| C'_i\).
Kemudian, kita akan menggabungkan ciphertext diikuti dengan tag untuk mendapatkan \(C_i\).
Menggabungkan segmen
Terakhir, semua segmen digabungkan sebagai \(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\), yang merupakan ciphertext akhir.
Fungsi dekripsi
Dekripsi hanya membalikkan enkripsi. Kita menggunakan header untuk mendapatkan nonce, dan mendekripsi setiap segmen teks tersandi satu per satu.
API mungkin (dan biasanya memang) mengizinkan akses acak, atau akses ke awal file tanpa memeriksa akhir file. Hal ini dimaksud karena Anda dapat mendekripsi \(M_i\) dari \(C_i\), tanpa mendekripsi semua blok ciphertext sebelumnya dan yang tersisa.
Namun, API harus berhati-hati agar tidak memungkinkan pengguna untuk salah memahami error akhir file dan dekripsi: dalam kedua kasus, API mungkin harus menampilkan error, dan mengabaikan perbedaannya dapat menyebabkan lawan dapat memotong file secara efektif.
Serialisasi dan penguraian kunci
Untuk melakukan serialisasi kunci dalam format "Tink Proto", pertama-tama kami memetakan parameter
dengan cara yang jelas ke dalam proto yang diberikan di
aes_ctr_hmac_streaming.proto.
Kolom version
harus ditetapkan ke 0.
Kemudian, kami melakukan serialisasi ini menggunakan serialisasi proto normal, dan menyematkan string yang dihasilkan ke nilai kolom proto KeyData. Kita menetapkan kolom
type_url
ke type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey
.
Kemudian, kita menetapkan key_material_type
ke SYMMETRIC
, dan menyematkannya ke dalam keyset. Kita biasanya menetapkan output_prefix_type
ke RAW
. Namun, jika kunci tersebut diuraikan dengan nilai yang berbeda yang ditetapkan untuk output_prefix_type
, Tink dapat menulis RAW
atau nilai sebelumnya.
Untuk mengurai kunci, kita membalikkan proses di atas (dengan cara biasa saat mengurai proto). Kolom key_material_type
diabaikan. Nilai
output_prefix_type
dapat diabaikan, atau kunci yang
memiliki output_prefix_type
yang berbeda dengan RAW
dapat ditolak.
Kunci yang memiliki version
yang berbeda dari 0 akan ditolak.
Referensi
-
[HRRV15] Hoang, Reyhanitabar, Rogaway, Vizar. Enkripsi yang diautentikasi secara online dan penanggulangan penyalahgunaannya dari penggunaan ulang nonce. CRYPTO 2015. https://eprint.iacr.org/2015/189 ↩
-
[HS20] Keamanan Enkripsi Streaming di Tink Library Google. Hoang, Shen, 2020. https://eprint.iacr.org/2020/1019 ↩
-
[HKDF] Extract-and-Expand Key Derivation Function (HKDF) berbasis HMAC, RFC 5869. https://www.rfc-editor.org/rfc/rfc5869 ↩