Liên kết phiên bản

Trong Keymaster 1, tất cả các khoá Keymaster đều được liên kết bằng mật mã với Gốc tin cậy của thiết bị hoặc khoá Xác minh quy trình khởi động. Trong Keymaster 2 và 3, tất cả các khoá cũng được liên kết với hệ điều hành và cấp bản vá của hình ảnh hệ thống. Điều này đảm bảo rằng kẻ tấn công phát hiện ra điểm yếu trong phiên bản cũ của phần mềm hệ thống hoặc TEE không thể khôi phục thiết bị về phiên bản dễ bị tấn công và sử dụng các khoá được tạo bằng phiên bản mới hơn. Ngoài ra, khi một khoá có phiên bản và cấp bản vá nhất định được sử dụng trên một thiết bị đã nâng cấp lên phiên bản hoặc cấp bản vá mới hơn, thì khoá đó sẽ được nâng cấp trước khi có thể sử dụng và phiên bản trước của khoá sẽ bị vô hiệu hoá. Theo cách này, khi thiết bị được nâng cấp, các khoá sẽ *tăng* cùng với thiết bị, nhưng mọi việc khôi phục thiết bị về bản phát hành trước đó đều khiến các khoá không sử dụng được.

Để hỗ trợ cấu trúc mô-đun của Treble và phá vỡ mối liên kết của system.img với boot.img, Keymaster 4 đã thay đổi mô hình liên kết phiên bản khoá để có các cấp bản vá riêng cho từng phân vùng. Điều này cho phép mỗi phân vùng được cập nhật độc lập, đồng thời vẫn cung cấp khả năng bảo vệ chống khôi phục về phiên bản cũ.

Để triển khai việc liên kết phiên bản này, ứng dụng đáng tin cậy (TA) KeyMint cần có cách để nhận một cách an toàn phiên bản hệ điều hành hiện tại và các cấp bản vá, đồng thời đảm bảo rằng thông tin mà ứng dụng này nhận được khớp với tất cả thông tin về hệ thống đang chạy.

  • Các thiết bị có tính năng Xác minh quy trình khởi động của Android (AVB) có thể đặt tất cả các cấp bản vá và phiên bản hệ thống trong vbmeta, để trình tải khởi động có thể cung cấp các cấp bản vá và phiên bản hệ thống đó cho Keymaster. Đối với các phân vùng được liên kết, thông tin phiên bản cho phân vùng nằm trong vbmeta được liên kết. Nói chung, thông tin phiên bản phải nằm trong vbmeta struct chứa dữ liệu xác minh (hàm băm hoặc cây hàm băm) cho một phân vùng nhất định.
  • Trên các thiết bị không có AVB:
    • Các cách triển khai Xác minh quy trình khởi động cần cung cấp hàm băm của siêu dữ liệu phiên bản cho trình tải khởi động, để trình tải khởi động có thể cung cấp hàm băm đó cho Keymaster.
    • boot.img có thể tiếp tục lưu trữ cấp bản vá trong tiêu đề
    • system.img có thể tiếp tục lưu trữ cấp bản vá và phiên bản hệ điều hành trong các thuộc tính chỉ đọc
    • vendor.img lưu trữ cấp bản vá trong thuộc tính chỉ đọc ro.vendor.build.version.security_patch.
    • Trình tải khởi động có thể cung cấp hàm băm của tất cả dữ liệu được Xác minh quy trình khởi động xác thực cho Keymaster.
  • Trong Android 9, hãy sử dụng các thẻ sau để cung cấp thông tin phiên bản cho các phân vùng sau:
    • VENDOR_PATCH_LEVEL: phân vùng vendor
    • BOOT_PATCH_LEVEL: phân vùng boot
    • OS_PATCH_LEVELOS_VERSION: phân vùng system. (OS_VERSION bị xoá khỏi tiêu đề boot.img
  • Các cách triển khai Keymaster phải xử lý tất cả các cấp bản vá một cách độc lập. Các khoá có thể sử dụng được nếu tất cả thông tin phiên bản khớp với các giá trị được liên kết với một khoá và IKeymaster::upgradeDevice() sẽ chuyển sang cấp bản vá cao hơn nếu cần.

Thay đổi HAL

Để hỗ trợ việc liên kết phiên bản và chứng thực phiên bản, Android 7.1 đã thêm các thẻ Tag::OS_VERSIONTag::OS_PATCHLEVEL cũng như các phương thức configureupgradeKey. Các thẻ phiên bản sẽ tự động được các cách triển khai Keymaster 2 trở lên thêm vào tất cả các khoá mới được tạo (hoặc cập nhật). Ngoài ra, mọi nỗ lực sử dụng khoá không có phiên bản hệ điều hành hoặc cấp bản vá khớp với phiên bản hệ điều hành hoặc cấp bản vá hiện tại của hệ thống sẽ bị từ chối bằng ErrorCode::KEY_REQUIRES_UPGRADE.

Tag::OS_VERSION là giá trị UINT đại diện cho các phần phiên bản lớn, phiên bản nhỏ và phụ-phụ của phiên bản hệ thống Android dưới dạng MMmmss, trong đó MM là phiên bản lớn, mm là phiên bản nhỏ và ss là phiên bản phụ-phụ. Ví dụ: 6.1.2 sẽ được biểu thị là 060102.

Tag::OS_PATCHLEVEL là giá trị UINT đại diện cho năm và tháng của bản cập nhật gần đây nhất cho hệ thống dưới dạng YYYYMM, trong đó YYYY là năm gồm 4 chữ số và MM là tháng gồm 2 chữ số. Ví dụ: tháng 3 năm 2016 sẽ được biểu thị là 201603.

UpgradeKey

Để cho phép nâng cấp các khoá lên phiên bản hệ điều hành mới và cấp bản vá của hình ảnh hệ thống, Android 7.1 đã thêm phương thức upgradeKey vào HAL:

Keymaster 3

    upgradeKey(vec keyBlobToUpgrade, vec upgradeParams)
        generates(ErrorCode error, vec upgradedKeyBlob);

Keymaster 2

keymaster_error_t (*upgrade_key)(const struct keymaster2_device* dev,
    const keymaster_key_blob_t* key_to_upgrade,
    const keymaster_key_param_set_t* upgrade_params,
    keymaster_key_blob_t* upgraded_key);
  • dev là cấu trúc thiết bị
  • keyBlobToUpgrade là khoá cần được nâng cấp
  • upgradeParams là các tham số cần thiết để nâng cấp khoá. Các tham số này bao gồm Tag::APPLICATION_IDTag::APPLICATION_DATA. Đây là các tham số cần thiết để giải mã blob khoá, nếu các tham số này được cung cấp trong quá trình tạo.
  • upgradedKeyBlob là tham số đầu ra, dùng để trả về blob khoá mới.

Nếu upgradeKey được gọi bằng một blob khoá không thể phân tích cú pháp hoặc không hợp lệ, thì phương thức này sẽ trả về ErrorCode::INVALID_KEY_BLOB. Nếu phương thức này được gọi bằng một khoá có cấp bản vá lớn hơn giá trị hiện tại của hệ thống, thì phương thức này sẽ trả về ErrorCode::INVALID_ARGUMENT. Nếu phương thức này được gọi bằng một khoá có phiên bản hệ điều hành lớn hơn giá trị hiện tại của hệ thống và giá trị hệ thống khác 0, thì phương thức này sẽ trả về ErrorCode::INVALID_ARGUMENT. Bạn có thể nâng cấp phiên bản hệ điều hành từ khác 0 thành 0. Trong trường hợp xảy ra lỗi khi giao tiếp với thế giới bảo mật, phương thức này sẽ trả về một giá trị lỗi thích hợp (ví dụ: ErrorCode::SECURE_HW_ACCESS_DENIED, ErrorCode::SECURE_HW_BUSY). Nếu không, phương thức này sẽ trả về ErrorCode::OK và trả về một blob khoá mới trong upgradedKeyBlob.

keyBlobToUpgrade vẫn hợp lệ sau khi gọi upgradeKey và về mặt lý thuyết, có thể được sử dụng lại nếu thiết bị bị hạ cấp. Trong thực tế, kho lưu trữ khoá thường gọi deleteKey trên blob keyBlobToUpgrade ngay sau khi gọi upgradeKey. Nếu keyBlobToUpgrade có thẻ Tag::ROLLBACK_RESISTANT, thì upgradedKeyBlob cũng phải có thẻ này (và phải có khả năng chống khôi phục về phiên bản cũ).

Cấu hình an toàn

Để triển khai việc liên kết phiên bản, TA Keymaster cần có cách để nhận một cách an toàn phiên bản hệ điều hành hiện tại và cấp bản vá (thông tin phiên bản), đồng thời đảm bảo rằng thông tin mà ứng dụng này nhận được khớp với thông tin về hệ thống đang chạy.

Để hỗ trợ việc phân phối thông tin phiên bản một cách an toàn cho TA, một OS_VERSION trường đã được thêm vào tiêu đề hình ảnh khởi động. Tập lệnh tạo hình ảnh khởi động sẽ tự động điền trường này. Các OEM và người triển khai TA Keymaster cần phối hợp với nhau để sửa đổi trình tải khởi động của thiết bị nhằm trích xuất thông tin phiên bản từ hình ảnh khởi động và chuyển thông tin đó cho TA trước khi hệ thống không an toàn được khởi động. Điều này đảm bảo rằng kẻ tấn công không thể can thiệp vào việc cung cấp thông tin phiên bản cho TA.

Bạn cũng cần đảm bảo rằng hình ảnh hệ thống có cùng thông tin phiên bản với hình ảnh khởi động. Để đạt được mục tiêu đó, phương thức định cấu hình đã được thêm vào HAL Keymaster:

keymaster_error_t (*configure)(const struct keymaster2_device* dev,
  const keymaster_key_param_set_t* params);

Đối số params chứa Tag::OS_VERSIONTag::OS_PATCHLEVEL. Phương thức này được các ứng dụng keymaster2 gọi sau khi mở HAL, nhưng trước khi gọi bất kỳ phương thức nào khác. Nếu bất kỳ phương thức nào khác được gọi trước khi định cấu hình, thì TA sẽ trả về ErrorCode::KEYMASTER_NOT_CONFIGURED.

Lần đầu tiên configure được gọi sau khi thiết bị khởi động, phương thức này sẽ xác minh rằng thông tin phiên bản được cung cấp khớp với thông tin do trình tải khởi động cung cấp. Nếu thông tin phiên bản không khớp, configure sẽ trả về ErrorCode::INVALID_ARGUMENT và tất cả các phương thức Keymaster khác sẽ tiếp tục trả về ErrorCode::KEYMASTER_NOT_CONFIGURED. Nếu thông tin khớp, configure sẽ trả về ErrorCode::OK và các phương thức Keymaster khác sẽ bắt đầu hoạt động bình thường.

Các lệnh gọi tiếp theo đến configure sẽ trả về cùng một giá trị do lệnh gọi đầu tiên trả về và không thay đổi trạng thái của Keymaster.

configure được gọi bởi hệ thống mà nội dung của hệ thống đó được dùng để xác thực, nên kẻ tấn công có một khoảng thời gian ngắn để xâm phạm hình ảnh hệ thống và buộc hệ thống cung cấp thông tin phiên bản khớp với hình ảnh khởi động, nhưng không phải là phiên bản thực tế của hệ thống. Việc kết hợp xác minh hình ảnh khởi động, xác thực dm-verity đối với nội dung hình ảnh hệ thống và thực tế là configure được gọi rất sớm trong quá trình khởi động hệ thống sẽ khiến kẻ tấn công khó khai thác khoảng thời gian này.