Tài liệu này xác định chính thức hàm toán học được biểu thị bằng khoá Truyền trực tuyến HMAC AES-CTR (được mã hoá ở định dạng proto là type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey
).
Phương thức mã hoá này dựa trên [HRRV15]1. Để phân tích tính bảo mật, chúng ta tham khảo [HS20]2. Ngoài ra, hãy lưu ý rằng các kiểm thử đa ngôn ngữ của Tink có một kiểm thử aes_ctr_hmac_streaming_key_test.py chứa test_manually_created_test_vector
với hướng dẫn đầy đủ về cách lấy văn bản đã mã hoá.
Khoá và thông số
Khoá được mô tả bằng các phần sau (tất cả kích thước trong tài liệu này đều tính bằng kibibyte):
- \(\mathrm{InitialKeyMaterial}\), một chuỗi byte: tài liệu khoá ban đầu.
- \(\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}\).
Ngoài ra, khoá hợp lệ phải đáp ứng các thuộc tính sau:
- \(\mathrm{len}(\mathrm{InitialKeyMaterial}) \geq \mathrm{DerivedKeySize}\).
- Nếu \(\mathrm{HmacHashType} = \mathrm{SHA1}\) thì \(\mathrm{HmacTagSize} \in \{10, \ldots, 20\}\).
- Nếu \(\mathrm{HmacHashType} = \mathrm{SHA256}\) thì \(\mathrm{HmacTagSize} \in \{10, \ldots, 32\}\).
- Nếu \(\mathrm{HmacHashType} = \mathrm{SHA512}\) thì \(\mathrm{HmacTagSize} \in \{10, \ldots, 64\}\).
- \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + \mathrm{HmacTagSize} + 8\) (Giá trị này bằng với\(\mathrm{len}(\mathrm{Header}) + \mathrm{HmacTagSize}\) như giải thích sau).
Các khoá không đáp ứng bất kỳ thuộc tính nào trong số này sẽ bị Tink từ chối (khi khoá được phân tích cú pháp hoặc khi tạo nguyên hàm tương ứng).
Hàm mã hoá
Để mã hoá một thông báo \(\mathrm{Msg}\) có dữ liệu liên kết \(\mathrm{AssociatedData}\), chúng ta tạo một tiêu đề, chia thông báo thành các phân đoạn, mã hoá từng phân đoạn và nối các phân đoạn. Chúng tôi giải thích các bước này như sau.
Tạo tiêu đề
Để tạo tiêu đề, trước tiên, chúng ta chọn một chuỗi ngẫu nhiên đồng nhất \(\mathrm{Salt}\)có độ dài \(\mathrm{DerivedKeySize}\). Tiếp theo, chúng ta chọn một chuỗi ngẫu nhiên đồng nhất\(\mathrm{NoncePrefix}\) có độ dài 7.
Sau đó, chúng ta đặt\(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\), trong đó độ dài của tiêu đề được mã hoá dưới dạng một byte. Chúng ta lưu ý rằng\(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\).
Tiếp theo, chúng ta sử dụng HKDF3 với hàm băm \(\mathrm{HkdfHashType}\) để tính toán nội dung khoá có độ dài \(\mathrm{DerivedKeySize} + 32\) cho thông báo này: \(k := \mathrm{HKDF}(\mathrm{InitialKeyMaterial}, \mathrm{Salt}, \mathrm{AssociatedData})\). Các giá trị đầu vào được sử dụng trong các giá trị đầu vào tương ứng tương ứng của\(\mathrm{HKDF}\): \(\mathrm{InitialKeyMaterial}\) là \(\mathrm{ikm}\),\(\mathrm{Salt}\) là muối và\(\mathrm{AssociatedData}\) được dùng làm \(\mathrm{info}\).
Chuỗi \(k\) sau đó được chia thành hai phần \(k_1 \| k_2 := k\), chẳng hạn như \(\mathrm{len}(k_1) = \mathrm{DerivedKeySize}\) và \(\mathrm{len}(k_2) = 32\).
Phân tách thông báo
Tiếp theo, thông báo \(M\) được chia thành các phần: \(M = M_0 \| M_1 \| \cdots \| M_{n-1}\).
Độ dài của các đoạn này được chọn sao cho thỏa mãn:
- \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{HmacTagSize}\}\).
- Nếu \(n > 1\), thì \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{HmacTagSize}\}\).
- Nếu \(n > 1\), thì \(M_{0}, \ldots, M_{n-2}\) phải có độ dài tối đa theo các quy tắc ràng buộc ở trên.
Trong quá trình phân tách này, \(n\) tối đa có thể là \(2^{32}\). Nếu không, quá trình mã hoá sẽ không thành công.
Mã hoá các khối
Để mã hoá phân đoạn \(M_i\), trước tiên, chúng ta tính toán
\(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b \| 0x00000000\), trong đó chúng ta mã hoá \(\mathrm{i}\) trong 4
byte bằng cách sử dụng phương thức mã hoá big-endian và đặt byte $b$ thành 0x00
nếu $i < n-1$
và 0x01
nếu không.
Sau đó, chúng ta mã hoá \(M_i\) bằng khoá AES CTR \(k_1\)và vectơ khởi tạo \(\mathrm{IV}_i\). Nói cách khác, dữ liệu đầu vào cho các lệnh gọi AES là\(\mathrm{IV}_i, \mathrm{IV}_i + 1, \mathrm{IV}_i + 2, \ldots\), trong đó \(\mathrm{IV}_i\) được diễn giải là số nguyên big-endian. Thao tác này sẽ trả về \(C'_i\).
Chúng ta tính toán thẻ bằng HMAC với hàm băm do \(\mathrm{HmacHashType}\) cung cấp và với khoá \(k_2\) trên chuỗi nối\(\mathrm{IV}_i \| C'_i\).
Sau đó, chúng ta nối văn bản đã mã hoá theo sau là thẻ để lấy \(C_i\).
Kết hợp các phân đoạn
Cuối cùng, tất cả các phân đoạn được nối với nhau dưới dạng\(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\), đây là văn bản đã mã hoá cuối cùng.
Hàm giải mã
Quá trình giải mã chỉ cần đảo ngược quá trình mã hoá. Chúng ta sử dụng tiêu đề để lấy số chỉ dùng một lần và giải mã từng phân đoạn văn bản đã mã hoá.
API có thể (và thường là) cho phép truy cập ngẫu nhiên hoặc truy cập vào đầu tệp mà không cần kiểm tra phần cuối tệp. Đây là điều có chủ ý vì bạn có thể giải mã \(M_i\) từ \(C_i\)mà không cần giải mã tất cả các khối văn bản đã mã hoá trước đó và còn lại.
Tuy nhiên, các API phải cẩn thận để không cho phép người dùng nhầm lẫn lỗi kết thúc tệp và lỗi giải mã: trong cả hai trường hợp, API có thể phải trả về lỗi và việc bỏ qua sự khác biệt có thể khiến đối thủ có thể cắt bớt tệp một cách hiệu quả.
Tuần tự hoá và phân tích cú pháp khoá
Để chuyển đổi tuần tự một khoá ở định dạng "Tink Proto", trước tiên, chúng ta ánh xạ các tham số một cách rõ ràng vào proto được cung cấp tại aes_ctr_hmac_streaming.proto.
Bạn cần đặt trường version
thành 0.
Sau đó, chúng ta sẽ chuyển đổi tuần tự thông tin này bằng cách chuyển đổi tuần tự proto thông thường và nhúng chuỗi kết quả vào giá trị của trường của proto KeyData. Chúng ta đặt trường type_url
thành type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey
.
Sau đó, chúng ta đặt key_material_type
thành SYMMETRIC
và nhúng giá trị này vào một tập hợp khoá. Chúng ta thường đặt output_prefix_type
thành RAW
. Trường hợp ngoại lệ là nếu khoá được phân tích cú pháp bằng một giá trị khác được đặt cho output_prefix_type
, thì Tink có thể ghi RAW
hoặc giá trị trước đó.
Để phân tích cú pháp một khoá, chúng ta đảo ngược quy trình trên (theo cách thông thường khi phân tích cú pháp proto). Trường key_material_type
sẽ bị bỏ qua. Bạn có thể bỏ qua giá trị của output_prefix_type
hoặc từ chối các khoá có output_prefix_type
khác với RAW
.
Các khoá có version
khác 0 sẽ bị từ chối.
Tài liệu tham khảo
-
[HRRV15] Hoang, Reyhanitabar, Rogaway, Vizar. Mã hoá xác thực trực tuyến và khả năng chống lại hành vi sử dụng sai số chỉ dùng một lần. CRYPTO 2015. https://eprint.iacr.org/2015/189 ↩
-
[HS20] Bảo mật của tính năng Mã hoá trực tuyến trong Thư viện Tink của Google. Hoang, Shen, 2020. https://eprint.iacr.org/2020/1019 ↩
-
[HKDF] Hàm dẫn xuất khoá trích xuất và mở rộng dựa trên HMAC (HKDF), RFC 5869. https://www.rfc-editor.org/rfc/rfc5869 ↩