Chìa khoá bọc phần cứng

Giống như hầu hết phần mềm mã hoá ổ đĩa và tệp, tính nă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 quá trình mã hoá. Ngay cả khi quá trình mã hoá do phần cứng chuyên dụng thực hiện thay vì phần mềm, phần mềm thường vẫn cần quản lý các khoá mã hoá thô.

Thông thường, đây không phải là vấn đề vì các khoá sẽ không xuất hiện trong một 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, mong muốn cung cấp tăng cường khả năng bảo vệ trước các loại tấn công khác, chẳng hạn như cold boot (khởi động nguội) các cuộc tấn công và cuộc tấn công trực tuyến mà trong đó kẻ tấn công có thể rò rỉ hệ thống mà không gây ảnh hưởng hoàn toàn đến thiết bị.

Để giải quyết vấn đề này, Android 11 đã ra mắt tính năng hỗ trợ dành cho khoá gói phần cứng (có hỗ trợ phần cứng). Khoá bọc phần cứng là khoá lưu trữ chỉ được xác định ở dạng thô để phần cứng chuyên dụng; phần mềm chỉ nhìn thấy và làm việc với các phím này được bọc (được mã hoá). Phần cứng này phải có khả năng tạo và nhập khoá lưu trữ, khoá lưu trữ gói ở dạng tạm thời và dài hạn, lấy từ khoá con, trực tiếp lập trình một khoá con thành một công cụ mã hoá cùng dòng, 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 (hay công cụ mã hoá nội tuyến) phần cứng mã hoá) đề cập đến phần cứng mã hoá/giải mã dữ liệu trong khi ảnh đang được truyền đến/từ thiết bị lưu trữ. Thông thường, đây là máy chủ UFS hoặc eMMC bộ điều khiển triển khai các tiện ích Crypto được xác định bằng Quy cách của JEDEC.

Thiết kế

Phần này trình bày thiết kế của tính năng khoá được gói bằng phần cứng, bao gồm cả phần cứng cần hỗ trợ cho tính năng này. Cuộc 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 khoá mã hoá thô trong bộ nhớ hệ thống là chỉ đặt chúng trong các ô khoá của một công cụ mã hoá cùng dòng. Tuy nhiên, việc này phương pháp tiếp cận 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 khoá.
  • Công cụ mã hoá nội tuyến chỉ có thể dùng để mã hoá/giải mã toàn bộ khối trên đĩa. Tuy nhiên, trong trường hợp của FBE, phần mềm vẫn cần phải có khả năng thực hiện công việc mật mã khác như mã hoá tên tệp và khoá dẫn xuất giá trị nhận dạng. Phần mềm vẫn cần quyền truy cập vào các khoá FBE thô để thực hiện công việc khác này.

Để tránh những sự cố này, khoá lưu trữ sẽ được chuyển thành khoá bọc phần cứng, là loại khoá mà chỉ có đối tượng mới có thể mở gói và sử dụng phần cứng chuyên dụng. Điều này cho phép hỗ trợ số lượng khoá không giới hạn. Ngoài ra, hệ 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ả về khoá con cho phần mềm đối với các 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 KDF (hàm dẫn xuất khoá) như HKDF, dẫn đến một hệ phân cấp khoá.

Biểu đồ dưới đây mô tả hệ phân cấp khoá điển hình cho FBE khi khoá bọc phần cứng không được sử dụng:

Hệ phân cấp khoá FBE (chuẩn)
Hình 1. Hệ phân cấp khoá FBE (chuẩn)

Khoá lớp FBE là khoá mã hoá thô mà Android truyền sang Linux để mở khoá một tập hợp thư mục đã mã hoá cụ thể, chẳng hạn như bộ nhớ được mã hoá thông tin xác thực cho một người dùng Android cụ thể. (Trong hạt 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à một giá trị dùng để xác định khoá bảo vệ một tệp hoặc thư mục cụ thể.
  • 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ệ phân cấp khoá cho FBE khi sử dụng khoá được gói bằng phần cứng:

Hệ phân cấp khoá FBE (với khoá được gói bằng phần cứng)
Hình 2. Hệ phân cấp khoá FBE (có khoá được gói bằng phần cứng)

So với trường hợp trước, khoá đã được thêm một cấp bổ sung và khoá mã hoá nội dung tệp đã được chuyển vị trí. Nút gốc vẫn đại diện cho khoá mà Android chuyển đến Linux để mở khoá một tập hợp các thư mục đã mã hoá. Tuy nhiên, hiện tại, khoá đó ở dạng được gói tạm thời và để sử dụng, khoá đó phải được chuyển đến phần cứng chuyên dụng. Phần cứng này phải triển khai hai giao diện lấy khoá được gói tạm thời:

  • Một giao diện để lấy inline_encryption_key và trực tiếp lập trình giao diện đó vào một khe khoá của công cụ mã hoá nội tuyến. Thao tác này cho phép tệp nội dung được mã hoá/giải mã mà không cần phần mềm có quyền truy cập vào dữ liệu thô . Trong các nhân phổ biến của Android, giao diện này tương ứng với Toán tử blk_crypto_ll_ops::keyslot_program, phải là do trình điều khiển bộ nhớ triển khai.
  • Một giao diện để lấy và trả về sw_secret ("mật khẩu phần mềm" – còn gọi là "mật khẩu thô" ở một số nơi), đây là khoá mà Linux sử dụng để lấy khoá con cho mọi thứ ngoại trừ việc mã hoá nội dung tệp. Trong các hạt nhân phổ biến của Android, giao diện này tương ứng với thao tác blk_crypto_ll_ops::derive_sw_secret, thao tác này phải được trình điều khiển bộ nhớ triển khai.

Để lấy inline_encryption_keysw_secret từ khoá bộ nhớ thô, phần cứng phải sử dụng KDF có độ mạnh về mặt mã hoá. KDF này phải tuân theo các phương pháp hay nhất về mật mã; phải có độ mạnh bảo mật tối thiểu là 256 bit, tức là đủ cho mọi thuật toán được sử dụng sau này. Mã này cũng phải sử dụng nhãn, ngữ cảnh và chuỗi thông tin dành riêng cho ứng dụng riêng biệt khi lấy từng loại khoá con để đảm bảo rằng các khoá con thu được được tách biệt bằng mật mã, tức là việc biết được một trong số đó không cho thấy bất kỳ khác. Không bắt buộc kéo giãn khoá vì khoá lưu trữ thô đã là phím ngẫu nhiên thố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 bảo mật. Tuy nhiên, đối với mục đích thử nghiệm, cần phải triển khai lại cùng một KDF trong mã kiểm thử. Hiện tại, một KDF đã được xem xét và triển khai; bạn có thể tìm thấy KDF này trong mã nguồn cho vts_kernel_encryption_test. Phần cứng nên sử dụng KDF này. KDF này sử dụng NIST SP 800-108 "KDF ở chế độ bộ đếm" với AES-256-CMAC làm PRF. Xin lưu ý rằng để tương thích, tất cả các phần của thuật toán phải giống nhau, bao gồm cả lựa chọn ngữ cảnh KDF và nhãn cho mỗi khoá con.

Bao bọc phím

Để đáp ứng mục tiêu bảo mật của khoá bọc phần cứng, có 2 kiểu gói khoá được xác định:

  • Gói tạm thời: phần cứng mã hoá khoá thô bằng cách sử dụ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.
  • Đóng gói dài hạn: phần cứng mã hoá khoá thô bằng một khoá duy nhất, ổn đị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 tới nhân hệ điều hành Linux để mở khoá bộ nhớ đều 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 khoá đang sử dụng trong bộ nhớ hệ thống thì không chỉ sử dụng được khoá đó ngoài 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ữ phiên bản đã mã hoá phím trên ổ đĩa để có thể mở khoá ngay từ đầu. Dữ liệu thô khoá sẽ hoạt động cho mục đích này. Tuy nhiên, bạn không nên để khoá thô xuất hiện trong bộ nhớ hệ thống để không bao giờ có thể trích xuất khoá thô để sử dụng ngoài thiết bị, ngay cả khi trích xuất tại thời điểm khởi động. Vì lý do này, khái niệm gói dài hạn được xác định.

Để hỗ trợ việc quản lý các khoá được gói theo hai 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ả lại chúng gói dài hạn. Các giao diện này được truy cập gián tiếp thông qua KeyMint và các thẻ này tương ứng với thẻ KeyMint TAG_STORAGE_KEY. vold sử dụng khả năng "tạo" để tạo khoá bộ nhớ mới cho Android sử dụng, còn vts_kernel_encryption_test sử dụng khả năng "nhập" để nhập khoá kiểm thử.
  • Giao diện để chuyển đổi khoá bộ nhớ được gói dài hạn thành khoá bộ nhớ được gói tạm thời. Điều này tương ứng với Phương thức KeyMint convertStorageKeyToEphemeral. Phương thức này được sử dụng bởi cả voldvts_kernel_encryption_test theo thứ tự để mở khoá bộ nhớ.

Thuật toán gói khoá là chi tiết triển khai, nhưng phải sử dụng AEAD mạnh như AES-256-GCM với IV ngẫu nhiên.

Yêu cầu thay đổi phần mềm

AOSP đã có khung cơ bản để hỗ trợ các khoá được gói phần cứng. Điều này bao gồm tính năng hỗ trợ trong các thành phần không gian người dùng như vold, cũng như tính năng hỗ trợ hạt nhân Linux trong blk-crypto, fscryptdm-default-key.

Tuy nhiên, bạn cần thực hiện một số thay đổi theo phương thức triển khai cụ thể.

Các thay đổi của KeyMint

Bạn phải sửa đổi cách 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 sử dụng thay vì convertStorageKeyToEphemeral.

Thay đổi về nhân Linux

Bạn phải sửa đổi trình điều khiển hạt nhân Linux cho công cụ mã hoá nội tuyến của thiết bị để hỗ trợ các khoá được gói bằng phần cứng.

Đối với các hạt 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_programblk_crypto_ll_ops::keyslot_evict hỗ trợ lập trình/loại bỏ các khoá được gói bằng phần cứng và triển khai blk_crypto_ll_ops::derive_sw_secret.

Đối với các hạt nhân android12android13, đặt BLK_CRYPTO_FEATURE_WRAPPED_KEYS trong blk_keyslot_manager::features, tạo blk_ksm_ll_ops::keyslot_programblk_ksm_ll_ops::keyslot_evict hỗ trợ lập trình/loại bỏ khoá được gói phần cứng, và triển khai blk_ksm_ll_ops::derive_raw_secret.

Đối với nhân android11, hãy đặt BLK_CRYPTO_FEATURE_WRAPPED_KEYS trong keyslot_manager::features, tạo keyslot_mgmt_ll_ops::keyslot_programkeyslot_mgmt_ll_ops::keyslot_evict hỗ trợ lập trình/loại bỏ các khoá được gói bằng phần cứng và triển khai keyslot_mgmt_ll_ops::derive_raw_secret.

Thử nghiệm

Mặc dù phương thức mã hoá bằng khoá bọc phần cứng khó kiểm tra hơn so với phương thức mã hoá bằng các khoá tiêu chuẩn, bạn vẫn có thể kiểm tra bằng cách nhập khoá kiểm tra và triển khai lại quy trình dẫn xuất 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 gói bằng phần cứng (ví dụ: FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicyDmDefaultKeyTest.TestHwWrappedKey) không bị bỏ qua do không phát hiện được tính năng hỗ trợ khoá được gói bằng phần cứng, vì kết quả kiểm thử vẫn là "đã vượt qua" trong trường hợp đó.

Bật phím

Sau khi tính năng hỗ trợ khoá được gói bằng phần cứng của thiết bị hoạt động đúng cách, bạn có thể 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 đó 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ụng fileencryption=::inlinecrypt_optimized+wrappedkey_v0. Để để biết thêm chi tiết, hãy xem FBE .
  • Mã hoá siêu dữ liệu: thêm cờ wrappedkey_v0 vào tham số metadata_encryption. Ví dụ: sử dụng metadata_encryption=:wrappedkey_v0. Để biết thêm thông tin, hãy xem tài liệu về việc mã hoá siêu dữ liệu.