Full disk encryption

Android full disk encryption 的Flow,這裡已知的東西就沒再提,這裡主要介紹Full disk encryption用的key到底是從哪裡取得,存放在哪裡,這裡分成Google以及Qualcomm extended from google 兩種方式。

 

Google:

網路上這張圖詮釋得很清楚,和Vold裡的source code都有match到。我們就直接從Vold的create_encrypted_random_ke -> encrypt_master_key開始:


  1.  一開始generate出16 byte, random的disk encryption key(DEK) ,google取名叫做master key,和16 byte salt

  2. Scrypt user password "default_password" 和 salt 變成32byte intermediate key 1 (IK1) 

Scrypt 為一種crypto function defined from external/scrypt/lib/crypto/crypto_scrypt.h

  1. 在32byte IK1前面加一個byte "00" 在後面補上223個byte "00" 變成padded, 256byte IK1

接著需要使用HBK(Hardware-bound key)去 sign (padded, 256byte) IK1,首先需要先說明HBK怎麼來的:

這裡我是看msm8916 NON-HLOS 那一包裡有一份keymaster source code可以大概猜到Qcom 的HBK(Hardware-bound key)怎麼取得的,在msm8956, msm8976, msm8996之後的案子都已經變成keymaster.mbn 也就是會被系統開機load 成 TA in Secure world。

首先需要在keymaster產生keyblob,如下:

  • 先gen出RSA key
  • 取得 Encryption HW key 透過qsee_kdf () 的 function => derived from SHK(Second hardware key),取得HMAC key透過qsee_kdf () 的 function => derived from SHK
  • Initial key blob (Create一個key blob 的struct)
  • 用 Encryption HW key加密 RSA 的private key存放在key blob 的 encrypted_private_exponent[KM_KEY_SIZE_MAX]
  • 用HMAC key 當作參數算出key blob的hmac 為 hmac[KM_HMAC_LENGTH](hmac的值根據我的了解就是算出keyblob這個struct(除了 hmac 這個field)的hash 並且用 HMAC key去做加密最後存放在keyblob裡的 hmac field)
struct  qcom_km_key_blob {

  uint32_t magic_num;

  uint32_t version_num;

  uint8_t  modulus[KM_KEY_SIZE_MAX];

  uint32_t modulus_size;

  uint8_t  public_exponent[KM_KEY_SIZE_MAX];

  uint32_t public_exponent_size;

  uint8_t  iv[KM_IV_LENGTH];

  uint8_t  encrypted_private_exponent[KM_KEY_SIZE_MAX];

  uint32_t encrypted_private_exponent_size;

  uint8_t  hmac[KM_HMAC_LENGTH];

};

typedef struct  qcom_km_key_blob qcom_km_key_blob_t;
  1. 用HBK(Hardware-bound key) sign  padded, 256byte IK1 變成 256byte IK2

把padded, 256byte IK1丟到Secure world透過keymaster sign function取得Encryption HW key,HMAC key(SHK對了 才有辦法取得),用HMAC key算hash….bar bar…..比對hmac,確認hmac一樣後,用Encryption HW key解開keyblob裡的encrypted_private_exponent得到真正的private key,用RSA key 去Sign padded, 256byte IK1 才會變成256byte IK2。

  1. Scrypt IK2 和salt 變成32 byte IK3
  2. 前16bytes即為 KEK(Key encryption key), 後16bytes為IV
  3. 用KEK和IV加密DEK with AES_CBC 演算法(Cipher-block chaining) 變成EDEK
  4. Scrypt IK3 和salt 變成 32byte Scrypted IK
這一步是為了以下的註解

    /* Store the scrypt of the intermediate key, so we can validate if it's a

       password error or mount error when things go wrong.

       Note there's no need to check for errors, since if this is incorrect, we

       simply won't wipe userdata, which is the correct default behavior

    */
  1. 接著將EDEK, salt, IV, AES_CBC, Scrypted lk資訊存放在footer裡 (data partition 最後一塊)
struct crypt_mnt_ftr {
  ….
  ….
  ….
  __le32 crypt_type;    /* how master_key is encrypted. Must be a
                         * CRYPT_TYPE_XXX value */
  __le64 fs_size;       /* Size of the encrypted fs, in 512 byte sectors */
  __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
                                  mount, set to 0 on successful mount */
  unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
                                                               needed to decrypt this
                                                               partition, null terminated */
  unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
  unsigned char salt[SALT_LEN];   /* The salt used for this encryption */

  __le8  kdf_type; /* The key derivation function used. */

  __le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
  __le32 keymaster_blob_size;

unsigned char scrypted_intermediate_key[SCRYPT_LEN];
};
這裡可以仔細看有keymaster_blob這個東西,這就是為了HBK而存放的。
  1. 當要進行data加密時,就是取出footer裡EDEK,然後一步一步解密得到DEK(disk encryption key),也就是google稱的master key,從data partition取出一個個sector,用DEK with AES-CBC and IV:SHA256加密完,再存回去。

Okay,Google就是停在上面第十步,就結束了。接下來要介紹Qualcomm extended from google,Vold不會走上面的第10步,會繼續往下做:

  1. 把IK3當作password 透過libQSEEComAPI.so的qseecom_create_key傳到secure world

一開始看Vold的code和Qualcomm的文件一直對不起來,Qualcomm的文件描述是user的password,code的命名方式也是這樣寫。所以會一直以為newpw是”default_password”,但印出來又是亂碼,但真正把全部印出來就會瞭解了。

SLOGE("wei passwd=%s",passwd);                       <=”default_password”
SLOGE("wei newpw_before=%s",newpw);         <=空的,沒東西

//這一個function最主要就是把passwd帶入,然後取得IK3
if (get_keymaster_hw_fde_passwd(passwd, newpw, crypt_ftr.salt, &crypt_ftr)){
  SLOGE("wei newpw=%s",newpw);
    key_index = set_hw_device_encryption_key(passwd, (char*)crypt_ftr.crypto_type_name);
}else{
    SLOGE("wei newpw=%s",newpw);

//第一次開機會是走這邊,把IK3帶入set_hw_device_encryption_key這function
    key_index = set_hw_device_encryption_key((const char*)newpw,  (char*) crypt_ftr.crypto_type_name);
}

in device/qcom/common/cryptfs_hw/cryptfs_hw.c
set_hw_device_encryption_key=> set_key=> qseecom_create_key  => kernel =>TZ barbarbar…
  1. TZ gen random key 為真正的DEK(disk encryption key),並update version in RPMB (透過RPMB Listener Service,QSEE4.0的那份文件有稍微介紹這個東西)

我猜也是丟到keymaster TA去,因為Qualcomm大部分跟key有關的function都是在keymaster去實作。

  1. Create random Salt, 算出IK3和Salt的hash當作KEK(Key encryption key) 並Generate random IV

KEK=hash(“IK3”, Salt)

  1. 拿KEK和IV with AES-GCM去加密DEK得到Encryption disk encryption key(EDEK)

Encrypt (AES-GCM, KEK, DEK, IV) => EDEK

  1. 存放version, Salt, IV, EDEK在SSD data struct

這裡的version其實就是為了和RPMB裡的version比對用來防止rollback。

  1. 將SSD data 用SHK加密存在 “ssd” partition
  • 這裡Qualcomm稱SHK為master key,一開始我還跟google的master key搞混,現在終於搞清楚,但我認為沒這麼簡單Qualcomm會直接用SHK去加密SSD data,應該是產生某個key derived from SHK 去加密SSD data。
  • 存在ssd partition應該是透過SSD Listener Service去將data存在ssd partition。
  1. Set 步驟12的DEK(disk encryption key) in ICE LUTs

Inline Crypto Engine

The ICE hardware currently supports AES-128/-256 bit ECB and XTS mode encryption algorithms. The keys for the crypto operations are loaded from the software. The keys are stored in a lookup table (LUT) located inside the ICE hardware. A maximum of 32 keys can be loaded in the ICE key LUT. A key inside the LUT can be referred via a key index.

最後就是從data partition取出一個個sector,用DEK with AES-XTS加密完,再存回去。

Done!!

Google的部分除了keyblob那一塊沒實際看到, 其他都沒問題。Qualcomm extended from google 的部分老實說很多都是拼湊起來的,大部分都沒有Source code,但覺得應該是滿接近的,不會有矛盾的情況。

例如: 如果在Setting裡設pin碼,我們不會看到data被重新加密,因為只是替換了keyblob 和 SSD data中的KEK而已,目前就Study 到這個階段,Vold最後call 到kernel req-dm-crypt的地方還沒有看過,之後有空再來看。