Android 5.0中的FDE功能实现

转载 2015年07月06日 16:14:10
标 题: 【分享】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;
}

相关文章推荐

FDE之加密流程

在Android M版本,手机都要求默认开启FDE,在download完之后的第一次开机过程中会对手机/data分区进行加密。其流程大致如下: FDE之加密流程 尽请关注手机安全之家,了...

FDE之默认密码开机流程

在Android M版本里,如果没有修改过锁屏密码,那android 系统就会用默认密码加密,默认密码加密之后的开机流程如下。 FDE之默认密码开机流程 尽请关注手机安全之家,了...

Android FDE 加密过程

Android FDE 实现原理

深入理解Android之设备加密Device Encryption

深入理解Android之设备加密Device EncryptionAndroid从4.4开始就支持一项功能,那就是对设备进行加密。加密自然是为了安全性考虑,由于/system目录是只读的,手机里那些存...
  • Innost
  • Innost
  • 2015年03月21日 19:25
  • 48046

FDE之要求密码开机流程

在Android M版本里,如果设定的加密密码,比如pin,password和pattern。那在开机的时候,会要求输入密码才能开机。 详细请参考: FDE之要求密码开机流程 请关注...

//转//Revisiting Android disk encryption

Revisiting Android disk encryption In iOS 8, Apple has expanded the scope of data encryption an...

android 5.0+录屏功能实现

Android Lollipop (5.0) 屏幕录制实现 基本原理在 Android 5.0,Google 终于开放了视频录制的接口,其实严格来说,是屏幕采集的接口,也就是 MediaProjec...

android5.0以上实现录屏功能,并将录屏内容在相册中显示!(unity调用android方法)

package com.Xreal.TJYH; import android.Manifest; import android.content.ContentValues; import andro...

Android 系统5.0以上,录屏功能

  • 2016年08月03日 10:35
  • 11.67MB
  • 下载

android5.0系统设置搜索功能简析

一、目的: 简单介绍系统设置搜索功能实现,初步熟悉搜索数据库构建规则以及匹配逻辑。 二、相关问题解答 1、系统设置可对那些设置项进行构建搜索数据库? 答:系统设置对数据项的构建规则在com.a...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android 5.0中的FDE功能实现
举报原因:
原因补充:

(最多只允许输入30个字)