Giống như hầu hết phần mềm mã hoá ổ đĩa và tệp, hoạt động mã hoá bộ nhớ của Android thường dựa vào các khoá mã hoá thô có trong bộ nhớ hệ thống để có thể thực hiện quy trình mã hoá. Ngay cả khi quá trình mã hoá được thực hiện bằng phần cứng chuyên dụng thay vì bằng phần mềm, phần mềm thường vẫn cần quản lý các khoá mã hoá thô.
Theo truyền thống, điều này không được coi là vấn đề vì các khoá không xuất hiện trong cuộc tấn công ngoại tuyến. Đây là loại tấn công chính mà tính năng mã hoá bộ nhớ nhằm mục đích bảo vệ. Tuy nhiên, chúng tôi muốn tăng cường khả năng bảo vệ trước các loại hình tấn công khác, chẳng hạn như tấn công khởi động nguội và các cuộc tấn công trên mạng, trong đó kẻ tấn công có thể làm rò rỉ bộ nhớ hệ thống mà không xâm nhập hoàn toàn vào thiết bị.
Để giải quyết vấn đề này, Android 11 đã giới thiệu tính năng hỗ trợ các khoá được bao bọc bằng phần cứng (nếu có hỗ trợ phần cứng). Khoá được bao bọc bằng phần cứng là các khoá lưu trữ mà chỉ phần cứng chuyên dụng mới biết ở dạng thô; phần mềm chỉ thấy và hoạt động với các khoá này ở dạng được bao bọc (mã hoá). Phần cứng này phải có khả năng tạo và nhập khoá lưu trữ, bao bọc khoá lưu trữ ở dạng tạm thời và dài hạn, lấy khoá con, lập trình trực tiếp một khoá con vào một công cụ mã hoá nội tuyến và trả về một khoá con riêng biệt cho phần mềm.
Lưu ý: Công cụ mã hoá nội tuyến (hoặc phần cứng mã hoá nội tuyến) là phần cứng mã hoá/giải mã dữ liệu trong khi dữ liệu đang trên đường đến/từ thiết bị lưu trữ. Thông thường, đây là bộ điều khiển máy chủ UFS hoặc eMMC triển khai các tiện ích mã hoá do quy cách JEDEC tương ứng xác định.
Thiết kế
Phần này trình bày thiết kế của tính năng khoá được bao bọc bằng phần cứng, bao gồm cả những yêu cầu về phần cứng để hỗ trợ tính năng này. Thảo luận này tập trung vào mã hoá dựa trên tệp (FBE), nhưng giải pháp này cũng áp dụng cho mã hoá siêu dữ liệu.
Một cách để tránh cần đến các khoá mã hoá thô trong bộ nhớ hệ thống là chỉ giữ các khoá này trong các khe khoá của một công cụ mã hoá nội tuyến. Tuy nhiên, phương pháp này gặp phải một số vấn đề:
- Số lượng khoá mã hoá có thể vượt quá số lượng khe cắm khoá.
- Các công cụ mã hoá nội tuyến thường mất nội dung của các khe cắm khoá nếu bộ điều khiển máy chủ lưu trữ được đặt lại. Đặt lại bộ điều khiển máy chủ lưu trữ là một quy trình khôi phục lỗi tiêu chuẩn được thực thi nếu xảy ra một số loại lỗi lưu trữ nhất định và những lỗi như vậy có thể xảy ra bất cứ lúc nào. Do đó, khi sử dụng tính năng mã hoá nội tuyến, hệ điều hành phải luôn sẵn sàng lập trình lại các khoá mà không cần người dùng can thiệp.
- Bạn chỉ có thể dùng các công cụ mã hoá nội tuyến để mã hoá/giải mã toàn bộ các khối dữ liệu trên ổ đĩa. Tuy nhiên, trong trường hợp FBE, phần mềm vẫn cần có khả năng thực hiện các công việc mã hoá khác, chẳng hạn như mã hoá tên tệp và lấy mã nhận dạng khoá. Phần mềm vẫn cần có quyền truy cập vào các khoá FBE thô để thực hiện công việc này.
Để tránh những vấn đề này, các khoá lưu trữ sẽ được chuyển thành khoá được bao bọc bằng phần cứng. Chỉ phần cứng chuyên dụng mới có thể mở gói và sử dụng các khoá này. Điều này cho phép hỗ trợ số lượng khoá không giới hạn. Ngoài ra, hệ thống phân cấp khoá được sửa đổi và một phần được chuyển sang phần cứng này, cho phép trả khoá con về phần mềm cho những tác vụ không thể sử dụng công cụ mã hoá nội tuyến.
Hệ phân cấp khoá
Bạn có thể lấy khoá từ các khoá khác bằng cách sử dụng hàm phái sinh khoá (KDF), chẳng hạn như HKDF, dẫn đến hệ thống phân cấp khoá.
Sơ đồ sau đây mô tả một hệ phân cấp khoá điển hình cho FBE khi các khoá được bao bọc bằng phần cứng không được sử dụng:
Khoá lớp FBE là khoá mã hoá thô mà Android truyền đến nhân Linux để mở khoá một nhóm thư mục được mã hoá cụ thể, chẳng hạn như bộ nhớ được mã hoá dành cho thông tin đăng nhập của một người dùng Android cụ thể. (Trong nhân, khoá này được gọi là khoá chính fscrypt.) Từ khoá này, nhân sẽ lấy các khoá con sau:
- Giá trị nhận dạng khoá. Giá trị này không dùng để mã hoá mà là giá trị dùng để xác định khoá mà một tệp hoặc thư mục cụ thể được bảo vệ.
- Khoá mã hoá nội dung tệp
- Khoá mã hoá tên tệp
Ngược lại, sơ đồ sau đây mô tả hệ thống phân cấp khoá cho FBE khi các khoá được bao bọc bằng phần cứng được dùng:
So với trường hợp trước đó, một cấp độ bổ sung đã được thêm vào hệ thống phân cấp khoá và khoá mã hoá nội dung tệp đã được di dời. Nút gốc vẫn đại diện cho khoá mà Android truyền đến Linux để mở khoá một nhóm thư mục được mã hoá. Tuy nhiên, khoá đó hiện ở dạng được bao bọc tạm thời và để sử dụng, khoá đó phải được truyền đến phần cứng chuyên dụng. Phần cứng này phải triển khai 2 giao diện lấy một khoá được bao bọc tạm thời:
- Một giao diện để lấy
inline_encryption_key
và lập trình trực tiếp vào một khe khoá của công cụ mã hoá nội tuyến. Điều này cho phép mã hoá/giải mã nội dung tệp mà không cần phần mềm có quyền truy cập vào khoá thô. Trong các nhân chung của Android, giao diện này tương ứng với thao tácblk_crypto_ll_ops::keyslot_program
. Trình điều khiển bộ nhớ phải triển khai thao tác này. - Một giao diện để lấy và trả về
sw_secret
("software secret" – còn được gọi là "raw secret" ở một số nơi), đây là khoá mà Linux dùng để lấy các khoá con cho mọi thứ khác ngoài việc mã hoá nội dung tệp. Trong các nhân chung của Android, giao diện này tương ứng với thao tácblk_crypto_ll_ops::derive_sw_secret
. Trình điều khiển bộ nhớ phải triển khai thao tác này.
Để lấy inline_encryption_key
và sw_secret
từ khoá lưu trữ thô, phần cứng phải sử dụng KDF mã hoá mạnh. KDF này phải tuân theo các phương pháp hay nhất về mật mã học; KDF phải có độ bảo mật ít nhất là 256 bit, tức là đủ cho mọi thuật toán được sử dụng sau này. Thư viện này cũng phải sử dụng nhãn và bối cảnh riêng biệt khi lấy từng loại khoá con để đảm bảo rằng các khoá con kết quả được cách ly về mặt mật mã, tức là kiến thức về một trong số các khoá con đó không tiết lộ bất kỳ khoá con nào khác. Không cần kéo dài khoá vì khoá lưu trữ thô đã là một khoá ngẫu nhiên đồng nhất.
Về mặt kỹ thuật, bạn có thể sử dụng bất kỳ KDF nào đáp ứng các yêu cầu về bảo mật.
Tuy nhiên, cho mục đích kiểm thử, vts_kernel_encryption_test
triển khai cùng một KDF trong phần mềm để tái tạo văn bản mã hoá trên đĩa và xác minh rằng văn bản đó là chính xác. Để dễ dàng kiểm thử và đảm bảo rằng KDF an toàn và đã được xem xét được sử dụng, phần cứng nên triển khai KDF mặc định mà hoạt động kiểm thử kiểm tra. Đối với phần cứng sử dụng một KDF khác, hãy xem phần Kiểm thử các khoá được bao bọc để biết cách định cấu hình kiểm thử cho phù hợp.
Bọc khoá
Để đáp ứng các mục tiêu bảo mật của khoá được bao bọc bằng phần cứng, hai loại bao bọc khoá được xác định:
- Bao bọc tạm thời: phần cứng mã hoá khoá thô bằng một khoá được tạo ngẫu nhiên ở mỗi lần khởi động và không được hiển thị trực tiếp bên ngoài phần cứng.
- Bọc khoá dài hạn: phần cứng mã hoá khoá thô bằng một khoá duy nhất, cố định được tích hợp vào phần cứng và không được hiển thị trực tiếp bên ngoài phần cứng.
Tất cả các khoá được truyền đến nhân Linux để mở khoá bộ nhớ đều được bao bọc tạm thời. Điều này đảm bảo rằng nếu kẻ tấn công có thể trích xuất một khoá đang dùng từ bộ nhớ hệ thống, thì khoá đó sẽ không dùng được không chỉ trên thiết bị mà còn trên thiết bị sau khi khởi động lại.
Đồng thời, Android vẫn cần có khả năng lưu trữ một phiên bản đã mã hoá của các khoá trên đĩa để có thể mở khoá chúng ngay từ đầu. Các khoá thô sẽ hoạt động cho mục đích này. Tuy nhiên, bạn nên không bao giờ để các khoá thô xuất hiện trong bộ nhớ hệ thống để chúng không bao giờ bị trích xuất để sử dụng bên ngoài thiết bị, ngay cả khi được trích xuất vào thời gian khởi động. Vì lý do này, khái niệm về việc bao bọc lâu dài được xác định.
Để hỗ trợ việc quản lý các khoá được bao bọc theo 2 cách khác nhau này, phần cứng phải triển khai các giao diện sau:
- Các giao diện để tạo và nhập khoá lưu trữ, trả về các khoá này ở dạng được bao bọc dài hạn. Các giao diện này được truy cập gián tiếp thông qua KeyMint và tương ứng với thẻ KeyMint
TAG_STORAGE_KEY
.vold
dùng khả năng "tạo" để tạo khoá lưu trữ mới cho Android sử dụng, cònvts_kernel_encryption_test
dùng khả năng "nhập" để nhập khoá kiểm thử. - Một giao diện để chuyển đổi khoá lưu trữ được bao bọc dài hạn thành khoá lưu trữ được bao bọc tạm thời. Điều này tương ứng với phương thức
convertStorageKeyToEphemeral
KeyMint. Phương thức này được cảvold
vàvts_kernel_encryption_test
dùng để mở khoá bộ nhớ.
Thuật toán gói khoá là một chi tiết triển khai, nhưng thuật toán này phải sử dụng AEAD mạnh, chẳng hạn như AES-256-GCM với IV ngẫu nhiên.
Cần thay đổi phần mềm
AOSP đã có một khung cơ bản để hỗ trợ các khoá được bao bọc bằng phần cứng. Điều này bao gồm cả sự hỗ trợ trong các thành phần không gian người dùng như vold
, cũng như sự hỗ trợ của nhân Linux trong blk-crypto, fscrypt và dm-default-key.
Tuy nhiên, bạn cần thực hiện một số thay đổi cụ thể về việc triển khai.
Thay đổi về KeyMint
Bạn phải sửa đổi việc triển khai KeyMint của thiết bị để hỗ trợ TAG_STORAGE_KEY
và triển khai phương thức convertStorageKeyToEphemeral
.
Trong Keymaster, exportKey
được dùng thay vì convertStorageKeyToEphemeral
.
Các thay đổi về nhân Linux
Bạn phải sửa đổi trình điều khiển nhân Linux cho công cụ mã hoá nội tuyến của thiết bị để hỗ trợ các khoá được bao bọc bằng phần cứng.
Đối với các nhân android14
trở lên, hãy đặt BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
trong blk_crypto_profile::key_types_supported
, tạo blk_crypto_ll_ops::keyslot_program
và blk_crypto_ll_ops::keyslot_evict
hỗ trợ lập trình/loại bỏ các khoá được bao bọc bằng phần cứng và triển khai blk_crypto_ll_ops::derive_sw_secret
.
Đối với các nhân android12
và android13
, hãy đặt BLK_CRYPTO_FEATURE_WRAPPED_KEYS
trong blk_keyslot_manager::features
, tạo blk_ksm_ll_ops::keyslot_program
và blk_ksm_ll_ops::keyslot_evict
hỗ trợ lập trình/loại bỏ các khoá được bao bọc bằng phần cứng và triển khai blk_ksm_ll_ops::derive_raw_secret
.
Đối với các nhân android11
, hãy đặt BLK_CRYPTO_FEATURE_WRAPPED_KEYS
trong keyslot_manager::features
, tạo keyslot_mgmt_ll_ops::keyslot_program
và keyslot_mgmt_ll_ops::keyslot_evict
hỗ trợ lập trình/loại bỏ các khoá được bao bọc bằng phần cứng và triển khai keyslot_mgmt_ll_ops::derive_raw_secret
.
Kiểm thử các khoá được bao bọc
Mặc dù khó kiểm thử hơn so với việc mã hoá bằng khoá thô, nhưng bạn vẫn có thể kiểm thử việc mã hoá bằng khoá được bao bọc bằng phần cứng bằng cách nhập khoá kiểm thử và triển khai lại quy trình phái sinh khoá mà phần cứng thực hiện. Việc này được triển khai trong vts_kernel_encryption_test
. Để chạy kiểm thử này, hãy chạy:
atest -v vts_kernel_encryption_test
Đọc nhật ký kiểm thử và xác minh rằng các trường hợp kiểm thử khoá được bao bọc bằng phần cứng (ví dụ: FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
và DmDefaultKeyTest.TestHwWrappedKey
) không bị bỏ qua do không phát hiện thấy sự hỗ trợ cho các khoá được bao bọc bằng phần cứng, vì kết quả kiểm thử vẫn là "đã vượt qua" trong trường hợp đó.
Theo mặc định, vts_kernel_encryption_test
giả định rằng phần cứng triển khai một KDF mà nó gọi là kdf1
. KDF này thuộc họ KDF ở chế độ bộ đếm của NIST SP 800-108 và sử dụng AES-256-CMAC làm hàm giả ngẫu nhiên. Để biết thêm thông tin về CMAC, hãy xem quy cách CMAC. KDF sử dụng các bối cảnh và nhãn cụ thể khi lấy từng khoá con. Phần cứng phải triển khai KDF này, bao gồm cả lựa chọn chính xác về ngữ cảnh, nhãn và định dạng của chuỗi đầu vào cố định khi lấy từng khoá con.
Tuy nhiên, vts_kernel_encryption_test
cũng triển khai các KDF bổ sung kdf2
thông qua kdf4
. Các khoá này cũng an toàn như kdf1
và chỉ khác nhau ở lựa chọn về ngữ cảnh, nhãn và định dạng của chuỗi đầu vào cố định. Chúng chỉ tồn tại để phù hợp với nhiều loại phần cứng.
Đối với những thiết bị sử dụng một KDF khác, hãy đặt thuộc tính hệ thống ro.crypto.hw_wrapped_keys.kdf
trong PRODUCT_VENDOR_PROPERTIES
thành tên của KDF như được xác định trong mã nguồn kiểm thử. Điều này khiến vts_kernel_encryption_test
kiểm tra KDF đó thay vì kdf1
. Ví dụ: để chọn kdf2
, hãy dùng:
PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2
Đối với những thiết bị sử dụng KDF mà quy trình kiểm thử không hỗ trợ, hãy thêm một quy trình triển khai KDF đó vào quy trình kiểm thử và đặt cho quy trình đó một tên riêng biệt.
Bật các khoá được bao bọc
Khi tính năng hỗ trợ khoá được bao bọc bằng phần cứng của thiết bị hoạt động bình thường, hãy thực hiện các thay đổi sau đối với tệp fstab
của thiết bị để Android sử dụng tệp này cho FBE và mã hoá siêu dữ liệu:
- FBE: thêm cờ
wrappedkey_v0
vào tham sốfileencryption
. Ví dụ: sử dụngfileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Để biết thêm thông tin chi tiết, hãy xem tài liệu về FBE. - Mã hoá siêu dữ liệu: thêm cờ
wrappedkey_v0
vào tham sốmetadata_encryption
. Ví dụ: sử dụngmetadata_encryption=:wrappedkey_v0
. Để biết thêm thông tin, hãy xem tài liệu về mã hoá siêu dữ liệu.