Android 5.0中的FDE功能實現

標 題: 【分享】Android 5.0中的FDE功能實現
作 者: netsniffer
時 間: 2015-01-27,21:15:06
鏈 接: http://bbs.pediy.com/showthread.php?t=197289

Android 5.0中的FDE(full disk encryption-全磁盤加密)

在Android 5.0之前,vold中的磁盤加密功能FDE(full disk encryption),只是對用戶密碼及SALT採用了scrypt算法來生成加密密鑰(4.4以前使用pkdf2,強度更低),這些只是增加了暴力破解的難度,但風險依然很高。比如可以獲取分區鏡像 前1K 及 後16K 的數據 (Android FDE的相關加密密鑰默認存儲在分區尾部的16 KBytes中,前4K是EXT4文件系統的Superblock,一般EXT4 FS SuperBlock前1K均爲0),暴力枚舉用戶密碼,並採用vold中相同的磁盤加密算法來生成解密密鑰,嘗試用其對前1K中的部分數據進行AES算法解密,解出來都爲0則碰撞OK。默認Android原生的用戶密碼6位數字密碼,即使很普通的PC 3、5個小時都能跑出來。

從5.0開始,增加了硬件TEE(Trusted Execution Environment)中的簽名過程,結合多次scrypt算法來產生加密密鑰,以此密鑰來加密 磁盤加密主密鑰,使得離線暴力破解幾乎無可能,除非破解者獲得了TEE中的RSA私鑰,並清楚特定硬件平臺HAL中的keymaster模塊實現。

5.0中的加密 磁盤加密密鑰 的實現邏輯:

1. 產生隨機16 Bytes DEK(disk encryption key--磁盤加密用的密鑰)及16 Bytes SALT;

2. 對(用戶密碼+SALT)使用scrypt算法產生32 Bytes HASH 作爲IK1(intermediate key 1);

3. 將IK1填充到硬件產生的私鑰規格大小(目前看到是RSA算法,256Bytes), 具體是: 
00 || IK1 || 00..00  ## one zero byte, 32 IK1 bytes, 223 zero bytes.

4. 使用硬件私鑰 HBK 對 IK1 進行簽名,生成256 Bytes簽名數據作爲IK2;

5. 對(IK2+SALT)使用scrypt算法(與第二步中的SALT相同)產生出32 Bytes HASH 作爲IK3;

6. 使用IK3前16 Bytes作爲KEK(用來加密主密鑰DEK的KEY),後16 Bytes作爲算法IV(初始化向量);

7. 使用AES_CBC算法,採用KEK作爲密鑰,IV作爲初始化向量來加密用戶的主密鑰DEK,生成加密後的主密鑰,存入分區尾部數據結構中;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

> 4.4之前的分區尾部 磁盤加密相關的存儲結構,默認採用pbkdf2來做密鑰導出算法
struct crypt_mnt_ftr {
  __le32 magic;    /* See above */
  __le16 major_version;
  __le16 minor_version;
  __le32 ftr_size;   /* in bytes, not including key following */
  __le32 flags;    /* See above */
  __le32 keysize;  /* in bytes */
  __le32 spare1;  /* ignored */
  __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 */
};

> 4.4中的分區尾部 磁盤加密相關的存儲結構,可選擇使用scrypt及pbkdf2,默認爲scrypt
struct crypt_mnt_ftr {
  __le32 magic;    /* See above */
  __le16 major_version;
  __le16 minor_version;
  __le32 ftr_size;   /* in bytes, not including key following */
  __le32 flags;    /* See above */
  __le32 keysize;  /* in bytes */
  __le32 spare1;  /* ignored */
  __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 */
  __le32 spare2;        /* ignored */
  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 */
  __le64 persist_data_offset[2];  /* Absolute offset to both copies of crypt_persist_data
                                   * on device with that info, either the footer of the
                                   * real_blkdevice or the metadata partition. */
  __le32 persist_data_size;       /* The number of bytes allocated to each copy of the
                                   * persistent data table*/
  __le8  kdf_type; /* The key derivation function used. */
  /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
  __le8  N_factor; /* (1 << N) */
  __le8  r_factor; /* (1 << r) */
  __le8  p_factor; /* (1 << p) */
};

> 5.0中默認採用基於TEE簽名外加多輪scrypt算法來導出加密密鑰
struct crypt_mnt_ftr {
  __le32 magic;         /* See above */
  __le16 major_version;
  __le16 minor_version;
  __le32 ftr_size;      /* in bytes, not including key following */
  __le32 flags;         /* See above */
  __le32 keysize;       /* in bytes */
  __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 */
  __le32 spare2;        /* ignored */
  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 */
  __le64 persist_data_offset[2];  /* Absolute offset to both copies of crypt_persist_data
                                   * on device with that info, either the footer of the
                                   * real_blkdevice or the metadata partition. */

  __le32 persist_data_size;       /* The number of bytes allocated to each copy of the
                                   * persistent data table*/

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

  /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
  __le8  N_factor; /* (1 << N) */
  __le8  r_factor; /* (1 << r) */
  __le8  p_factor; /* (1 << p) */
  __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
                            we have to stop (e.g. power low) this is the last
                            encrypted 512 byte sector.*/
  __le8  hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
                                                    set, hash of first block, used
                                                    to validate before continuing*/

  /* key_master key, used to sign the derived key which is then used to generate
   * the intermediate key
   * This key should be used for no other purposes! We use this key to sign unpadded 
   * data, which is acceptable but only if the key is not reused elsewhere. */
  __le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
  __le32 keymaster_blob_size;

  /* Store scrypt of salted intermediate key. When decryption fails, we can
     check if this matches, and if it does, we know that the problem is with the
     drive, and there is no point in asking the user for more passwords.

     Note that if any part of this structure is corrupt, this will not match and
     we will continue to believe the user entered the wrong password. In that
     case the only solution is for the user to enter a password enough times to
     force a wipe.

     Note also that there is no need to worry about migration. If this data is
     wrong, we simply won't recognise a right password, and will continue to
     prompt. On the first password change, this value will be populated and
     then we will be OK.
   */
  unsigned char scrypted_intermediate_key[SCRYPT_LEN];
};

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

幾種密鑰導出算法

static int pbkdf2(const char *passwd, const unsigned char *salt,
                  unsigned char *ikey, void *params UNUSED)
{
    SLOGI("Using pbkdf2 for cryptfs KDF");

    /* Turn the password into a key and IV that can decrypt the master key */
    unsigned int keysize;
    char* master_key = (char*)convert_hex_ascii_to_key(passwd, &keysize);
    if (!master_key) return -1;
    // 2000輪,顯然強度很低,所以4.4以後改成scrypt這種強度適中的算法
    PKCS5_PBKDF2_HMAC_SHA1(master_key, keysize, salt, SALT_LEN,
                           /*2000*/HASH_COUNT, /*16+16*/KEY_LEN_BYTES+IV_LEN_BYTES, ikey);

    memset(master_key, 0, keysize);
    free (master_key);
    return 0;
}

----------------------------------------------------

static int scrypt(const char *passwd, const unsigned char *salt,
                  unsigned char *ikey, void *params)
{
    SLOGI("Using scrypt for cryptfs KDF");

    struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
    
    int N = 1 << ftr->N_factor; // 15
    int r = 1 << ftr->r_factor; // 3
    int p = 1 << ftr->p_factor; // 1

    /* Turn the password into a key and IV that can decrypt the master key */
    unsigned int keysize;
    unsigned char* master_key = convert_hex_ascii_to_key(passwd, &keysize);
    if (!master_key) return -1;
    crypto_scrypt(master_key, keysize, salt, SALT_LEN, N, r, p, ikey,
            KEY_LEN_BYTES + IV_LEN_BYTES);

    memset(master_key, 0, keysize);
    free (master_key);
    return 0;
}

----------------------------------------------------

static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
                            unsigned char *ikey, void *params)
{
    SLOGI("Using scrypt with keymaster for cryptfs KDF");

    int rc;
    unsigned int key_size;
    size_t signature_size;
    unsigned char* signature;
    struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;

    int N = 1 << ftr->N_factor;
    int r = 1 << ftr->r_factor;
    int p = 1 << ftr->p_factor;

    unsigned char* master_key = convert_hex_ascii_to_key(passwd, &key_size);
    if (!master_key) {
        SLOGE("Failed to convert passwd from hex");
        return -1;
    }

    // 第一輪scrypt,生成IK1
    rc = crypto_scrypt(master_key, key_size, salt, SALT_LEN,
                       N, r, p, ikey, KEY_LEN_BYTES + IV_LEN_BYTES);
    memset(master_key, 0, key_size);
    free(master_key);

    if (rc) {
        SLOGE("scrypt failed");
        return -1;
    }

    // TEE中初始化RSA密鑰對,最終在TEE中對IK1進行簽名,並返回簽名結果作爲IK2
    if (keymaster_sign_object(ftr, ikey, KEY_LEN_BYTES + IV_LEN_BYTES,
                              &signature, &signature_size)) {
        SLOGE("Signing failed");
        return -1;
    }

    // 對IK2結合salt再進行一輪scrypt,產生最終的AES_CBC使用的ikey(16B KEK + 16B IV)
    rc = crypto_scrypt(signature, signature_size, salt, SALT_LEN,
                       N, r, p, ikey, KEY_LEN_BYTES + IV_LEN_BYTES);
    free(signature);

    if (rc) {
        SLOGE("scrypt failed");
        return -1;
    }

    return 0;
}

/* Create a new keymaster key and store it in this footer */
static int keymaster_create_key(struct crypt_mnt_ftr *ftr)
{
    uint8_t* key = 0;
    keymaster_device_t *keymaster_dev = 0;

    if (keymaster_init(&keymaster_dev)) {
        SLOGE("Failed to init keymaster");
        return -1;
    }

    int rc = 0;

    keymaster_rsa_keygen_params_t params;
    memset(&params, '\0', sizeof(params));
    params.public_exponent = RSA_EXPONENT;
    params.modulus_size = RSA_KEY_SIZE;

    size_t key_size;
    // 調用HAL中的加密模塊,產生RSA密鑰對
    // 其中的私鑰一般是加密返回的,具體keymaster_blob數據結構和算法不同硬件平臺廠家實現不一樣

    if (keymaster_dev->generate_keypair(keymaster_dev, TYPE_RSA, &params,
                                        &key, &key_size)) {
        SLOGE("Failed to generate keypair");
        rc = -1;
        goto out;
    }

    if (key_size > KEYMASTER_BLOB_SIZE) {
        SLOGE("Keymaster key too large for crypto footer");
        rc = -1;
        goto out;
    }

    memcpy(ftr->keymaster_blob, key, key_size);
    ftr->keymaster_blob_size = key_size;

out:
    keymaster_close(keymaster_dev);
    free(key);
    return rc;
}

static int encrypt_master_key(const char *passwd, const unsigned char *salt,
                              const unsigned char *decrypted_master_key,
                              unsigned char *encrypted_master_key,
                              struct crypt_mnt_ftr *crypt_ftr)
{
    unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
    EVP_CIPHER_CTX e_ctx;
    int encrypted_len, final_len;
    int rc = 0;

    /* Turn the password into an intermediate key and IV that can decrypt the master key */
    get_device_scrypt_params(crypt_ftr);

    switch (crypt_ftr->kdf_type) {
    case KDF_SCRYPT_KEYMASTER_UNPADDED:
    case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
    case KDF_SCRYPT_KEYMASTER:
        // 加載並初始化HAL keymaster模塊,調用TEE接口得到RSA的公私鑰對keymaster_blob
        if (keymaster_create_key(crypt_ftr)) {
            SLOGE("keymaster_create_key failed");
            return -1;
        }

        // 產生加密密鑰KEK
        if (scrypt_keymaster(passwd, salt, ikey, crypt_ftr)) {
            SLOGE("scrypt failed");
            return -1;
        }
        break;

    case KDF_SCRYPT:
        if (scrypt(passwd, salt, ikey, crypt_ftr)) {
            SLOGE("scrypt failed");
            return -1;
        }
        break;

    default:
        SLOGE("Invalid kdf_type");
        return -1;
    }

    // 使用前邊得到的KEK及IV,採用AES_CBC算法對磁盤加密密鑰master_key進行加密
    

    /* Initialize the decryption engine */
    if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
        SLOGE("EVP_EncryptInit failed\n");
        return -1;
    }
    EVP_CIPHER_CTX_set_padding(&e_ctx, 0); /* Turn off padding as our data is block aligned */

    /* Encrypt the master key */
    if (! EVP_EncryptUpdate(&e_ctx, encrypted_master_key, &encrypted_len,
                              decrypted_master_key, KEY_LEN_BYTES)) {
        SLOGE("EVP_EncryptUpdate failed\n");
        return -1;
    }
    if (! EVP_EncryptFinal(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) {
        SLOGE("EVP_EncryptFinal failed\n");
        return -1;
    }

    if (encrypted_len + final_len != KEY_LEN_BYTES) {
        SLOGE("EVP_Encryption length check failed with %d, %d bytes\n", encrypted_len, final_len);
        return -1;
    }

    /* 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
    */
    int N = 1 << crypt_ftr->N_factor;
    int r = 1 << crypt_ftr->r_factor;
    int p = 1 << crypt_ftr->p_factor;

    // 對ikey(KEK+IV)進行一次摘要存入分區尾部的crypt_ftr數據結構中,
    // 後續驗證可對傳入的用戶密碼+分區尾部存儲的SALT 採用同樣的密鑰導出算法,
    // 生成加密密鑰,並做一次scrypt摘要,將摘要值與之前crypt_ftr存儲的進行比較來檢驗是否一致

    rc = crypto_scrypt(ikey, KEY_LEN_BYTES,
                       crypt_ftr->salt, sizeof(crypt_ftr->salt), N, r, p,
                       crypt_ftr->scrypted_intermediate_key,
                       sizeof(crypt_ftr->scrypted_intermediate_key));

    if (rc) {
      SLOGE("encrypt_master_key: crypto_scrypt failed");
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章