Tink sử dụng Keyset để bật tính năng xoay khoá. Về mặt chính thức, một bộ khoá là một danh sách không trống 1 gồm các khoá, trong đó một khoá được chỉ định là khoá chính (khoá được dùng để ký và mã hoá văn bản thuần tuý mới). Ngoài ra, các khoá trong một bộ khoá sẽ có một mã nhận dạng duy nhất 2 và trạng thái khoá cho phép vô hiệu hoá các khoá mà không cần xoá chúng khỏi bộ khoá.
Keyset là cách chính mà người dùng có thể truy cập vào các khoá (thông qua lớp KeysetHandle). Điều này đảm bảo rằng mọi người dùng đều có mã để xử lý nhiều khoá cùng một lúc. Đối với hầu hết người dùng mật mã học, việc xử lý nhiều khoá là một nhu cầu: cần có thể thay đổi khoá (ví dụ: khoá cũ có thể bị rò rỉ) và hầu như không bao giờ có thao tác "chuyển sang khoá tiếp theo" nguyên tử có thể áp dụng cho các máy mà mã chạy và tất cả các văn bản mã hoá, trên toàn cầu và ngay lập tức. Do đó, người dùng cần viết mã hoạt động khi thay đổi từ khoá này sang khoá tiếp theo.
Ví dụ: AEAD
Hãy cân nhắc một tập hợp khoá AEAD chứa nhiều khoá cho nguyên thuỷ AEAD. Như đã giải thích trước đó, mỗi khoá chỉ định riêng biệt 2 hàm: \(\mathrm{Enc}\) và \(\mathrm{Dec}\). Giờ đây, keyset cũng chỉ định 2 hàm mới: \(\mathrm{Enc}\) và \(\mathrm{Dec}\) – \(\mathrm{Enc}\) chỉ đơn giản bằng hàm \(\mathrm{Enc}\) của khoá chính trong keyset, trong khi hàm \(\mathrm{Dec}\) cố gắng giải mã bằng tất cả các khoá, theo một thứ tự nào đó (xem bên dưới để biết cách Tink cải thiện hiệu suất của hàm này).
Điều thú vị cần lưu ý là Keysets là các khoá đầy đủ: chúng là nội dung mô tả đầy đủ về các hàm \(\mathrm{Enc}\) và\(\mathrm{Dec}\) được dùng. Điều này có nghĩa là người dùng có thể viết một lớp nhận KeysetHandle làm dữ liệu đầu vào, thể hiện ý tưởng rằng lớp này cần có nội dung mô tả đầy đủ về các đối tượng \(\mathrm{Enc}\) và \(\mathrm{Dec}\) để hoạt động đúng cách. Điều này cho phép người dùng viết các API giao tiếp rằng: để sử dụng lớp này, bạn cần cung cấp cho tôi nội dung mô tả về một nguyên tắc mật mã.
Xoay vòng khoá
Hãy xem xét một người dùng Tink, viết một chương trình trước tiên lấy một bộ khoá từ KMS, sau đó tạo một đối tượng AEAD từ bộ khoá này và cuối cùng sử dụng đối tượng này để mã hoá và giải mã văn bản mã hoá.
Người dùng như vậy sẽ tự động được chuẩn bị cho việc xoay vòng khoá và chuyển đổi thuật toán trong trường hợp lựa chọn hiện tại của họ không còn đáp ứng tiêu chuẩn nữa.
Tuy nhiên, bạn phải cẩn thận khi triển khai quy trình xoay vòng khoá như vậy: Trước tiên, KMS phải thêm một khoá mới vào bộ khoá (nhưng chưa đặt khoá đó làm khoá chính). Sau đó, bạn cần triển khai bộ khoá mới cho tất cả các tệp nhị phân để mọi tệp nhị phân sử dụng bộ khoá này đều có khoá mới nhất trong bộ khoá. Chỉ khi đó, khoá mới mới được đặt làm khoá chính và bộ khoá kết quả sẽ được phân phối lại cho tất cả các tệp nhị phân bằng bộ khoá.
Giá trị nhận dạng khoá trong văn bản mã hoá
Hãy xem xét lại ví dụ về một khoá AEAD. Nếu thực hiện một cách đơn giản, việc giải mã văn bản mã hoá yêu cầu Tink cố gắng giải mã bằng tất cả các khoá trong Keyset, vì không có cách nào biết được khoá nào đã được dùng để mã hoá keyset. Điều này có thể gây ra mức hao tổn hiệu suất lớn.
Vì lý do này, Tink cho phép thêm tiền tố vào văn bản mã hoá bằng một chuỗi 5 byte bắt nguồn từ mã nhận dạng. Theo triết lý "Khoá đầy đủ" ở trên, tiền tố này là một phần của khoá và tất cả các văn bản mã hoá từng được tạo bằng khoá này đều phải có tiền tố này. Khi tạo khoá, người dùng có thể chọn xem khoá có nên sử dụng tiền tố như vậy hay không, hoặc có nên sử dụng định dạng văn bản mã hoá mà không có tiền tố đó hay không.
Khi một khoá nằm trong một keyset, Tink sẽ tính toán thẻ này từ mã nhận dạng mà khoá có trong keyset. Việc mã nhận dạng là duy nhất2 trong một keyset ngụ ý rằng các thẻ là duy nhất. Do đó, nếu chỉ sử dụng các khoá được gắn thẻ, thì sẽ không có sự sụt giảm hiệu suất so với việc giải mã bằng một khoá duy nhất: Tink chỉ cần thử một trong các khoá khi giải mã.
Tuy nhiên, vì thẻ này là một phần của khoá, nên điều này cũng ngụ ý rằng khoá chỉ có thể nằm trong một bộ khoá nếu có một mã nhận dạng cụ thể. Điều này có một số ảnh hưởng khi mô tả việc triển khai các đối tượng chính bằng nhiều ngôn ngữ.
Các khoá có yêu cầu về mã nhận dạng nhưng không có tiền tố đầu ra
Một số khoá phải có một mã nhận dạng cụ thể nhưng không thêm tiền tố vào đầu ra. Ví dụ: các khoá chữ ký có biến thể NO_PREFIX_WITH_PREHASH_ID (được lưu trữ bằng loại tiền tố đầu ra WITH_ID_REQUIREMENT) tạo ra chữ ký không có tiền tố. Khi bạn sử dụng một khoá như vậy với nguyên tắc cơ bản Prehash, Tink sẽ ghi mã nhận dạng khoá vào giá trị tiền băm, để trình ký từ xa biết nên ký bằng khoá nào.
Tương tự như các khoá sử dụng tiền tố, khoá như vậy chỉ có thể nằm trong một bộ khoá theo một mã nhận dạng đó. Mã khoá trong giá trị tiền băm là siêu dữ liệu thuần tuý, giống như tiền tố đầu ra: chữ ký không liên kết mã khoá và trình xác minh không bao giờ thấy mã khoá. Để biết bố cục cấp byte, hãy xem Định dạng Tink wire.
-
Một số phần của Tink vẫn coi Keyset là một tập hợp. Tuy nhiên, bạn nên thay đổi chế độ này. Lý do là vì thứ tự thường rất quan trọng: ví dụ: hãy xem xét vòng đời điển hình của một quy trình xoay vòng khoá bằng Aead. Trước tiên, một khoá mới sẽ được thêm vào một bộ khoá. Khoá này chưa được đặt làm khoá chính nhưng đang hoạt động. Bộ khoá mới này được triển khai cho tất cả các tệp nhị phân. Sau khi tất cả các tệp nhị phân đều biết khoá mới, khoá đó sẽ trở thành khoá chính (chỉ khi đó việc sử dụng khoá này mới an toàn). Trong bước thứ hai này, tính năng xoay khoá cần biết khoá cuối cùng đã thêm. ↩
-
Để tương thích với một thư viện nội bộ của Google, Tink cho phép có các keyset trong đó các mã nhận dạng được lặp lại. Sau này, chúng tôi sẽ ngừng hỗ trợ tính năng này. ↩