Phát trực tuyến HMAC AES-CTR

Tài liệu này định nghĩa chính thức hàm toán học được biểu thị bằng các 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 linh hoạt dựa trên [HRRV15]1. Để phân tích về tính bảo mật, chúng tôi tham khảo [HS20]2. Ngoài ra, xin lưu ý rằng các chương trình kiểm thử trên nhiều ngôn ngữ Tink có một chương trình kiểm thử aes_ctr_hmac_streaming_key_test.py, chứa test_manually_created_test_vector kèm theo hướng dẫn đầy đủ về cách lấy bản mật mã.

Khoá và thông số

Các khoá được mô tả trong các phần sau (mọi kích thước trong tài liệu này đều tính bằng byte):

  • \(\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}\).

Khoá hợp lệ còn đáp ứng thêm các tính chất 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\(\mathrm{len}(\mathrm{Header}) + \mathrm{HmacTagSize}\) như sẽ giải thích ở phần sau).

Tink sẽ từ chối các khoá không đáp ứng bất kỳ thuộc tính nào trong số này (khi khoá được phân tích cú pháp hoặc khi dữ liệu gốc tương ứng được tạo).

Hàm mã hoá

Để mã hoá một thông báo \(\mathrm{Msg}\) bằng dữ liệu liên kết\(\mathrm{AssociatedData}\), chúng ta sẽ tạo một tiêu đề, chia thông báo đó thành nhiều 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 sẽ giải thích các bước này trong phần dưới đây.

Tạo tiêu đề

Để tạo tiêu đề, trước tiên, chúng ta sẽ 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 sẽ 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 tôi nhận thấy 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 chất liệu khoá có độ dài \(\mathrm{DerivedKeySize} + 32\) cho thông báo này: \(k := \mathrm{HKDF}(\mathrm{InitialKeyMaterial}, \mathrm{Salt}, \mathrm{AssociatedData})\). Dữ liệu đầu vào được dùng trong các giá trị đầu vào tương ứng là\(\mathrm{HKDF}\): \(\mathrm{InitialKeyMaterial}\) là \(\mathrm{ikm}\),\(\mathrm{Salt}\) là dữ liệu ngẫu nhiên và\(\mathrm{AssociatedData}\) được dùng làm \(\mathrm{info}\).

Sau đó, chuỗi \(k\) đượ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\).

Chia nhỏ thông báo

Tiếp theo, thông báo \(M\) sẽ được chia thành nhiều phần: \(M = M_0 \| M_1 \| \cdots \| M_{n-1}\).

Độ dài của chúng được chọn để đáp ứng:

  • \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{HmacTagSize}\}\).
  • Nếu là \(n > 1\), thì sẽ là \(\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 điều kiện ở trên để tuân thủ các điều kiện ràng buộc.

Trong phần phân chia này, \(n\) tối đa có thể là \(2^{32}\). Nếu không, quá trình mã hoá 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 tôi tính toán\(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b \| 0x00000000\), trong đó chúng tôi mã hoá \(\mathrm{i}\) theo 4 byte bằng phương thức mã hoá cuối lớn và đặt byte $b$ thành 0x00 nếu $i < n-1$ và 0x01 nếu không.

Sau đó, chúng tôi mã hoá \(M_i\) bằng khoá CTR AES \(k_1\)và vectơ khởi tạo\(\mathrm{IV}_i\). Nói cách khác, giá trị nhập vào khi gọi AES sẽ\(\mathrm{IV}_i, \mathrm{IV}_i + 1, \mathrm{IV}_i + 2, \ldots\), trong đó \(\mathrm{IV}_i\) được hiểu là số nguyên lớn. Kết quả là \(C'_i\).

Chúng tôi tính toán thẻ bằng cách sử dụng HMAC với hàm băm do \(\mathrm{HmacHashType}\) cung cấp và bằng khoá \(k_2\) qua phép nối\(\mathrm{IV}_i \| C'_i\).

Sau đó, chúng ta nối đoạn mật mã theo sau là thẻ để nhận được \(C_i\).

Nối các phân đoạn

Cuối cùng, tất cả các phân đoạn được nối dưới dạng\(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\), đây là bản mã hoá cuối cùng.

Chức năng giải mã

Giải mã chỉ đơn giản là đảo ngược quá trình mã hoá. Chúng tôi 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 của văn bản mật mã.

Các API có thể (và thường làm) 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. Việc này là có chủ ý vì 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 thuật toán mật mã 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 cuối 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 các tệp một cách hiệu quả.

Chuyển đổi tuần tự và phân tích cú pháp các khoá

Để chuyển đổi tuần tự một khoá ở định dạng "Tink Proto", trước tiên, chúng tôi á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 tôi chuyển đổi tuần tự bằng cách sử dụng quy trình chuyển đổi tuần tự proto thông thường và nhúng chuỗi kết quả vào giá trị trường của một proto KeyData. Chúng ta đặt trường type_url thành type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey. Sau đó, chúng ta thiết lập 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 tập hợp giá trị khác cho output_prefix_type, thì TIN có thể sẽ ghi RAW hoặc giá trị trước đó.

Để phân tích cú pháp một khoá, chúng tôi sẽ đảo ngược quy trình trên (theo cách thông thường khi phân tích cú pháp protos). Trường key_material_type sẽ bị bỏ qua. Bạn có thể bỏ qua giá trị của output_prefix_type hoặc các khoá có output_prefix_type khác với RAW có thể bị từ chối. Những khoá có version khác 0 sẽ bị từ chối.

Tài liệu tham khảo


  1. [HRRV15] Hoàng, Reyhanitabar, Rogaway, Vizar. Mã hoá được xác thực trực tuyến và khả năng chống sử dụng sai mục đích số chỉ dùng một lần. CRYPTO 2015. https://eprint.iacr.org/2015/189 

  2. [HS20] Bảo mật khi mã hoá nội dung phát trực tuyến trong Thư viện Tink của Google. Hoang, Shen, 2020. https://eprint.iacr.org/2020/1019 

  3. [HKDF] Hàm trích xuất và mở rộng khoá dựa trên HMAC (HKDF), RFC 5869. https://www.rfc-editor.org/rfc/rfc5869