标 题:  【分享】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;
}

Android 5.0中的FDE功能实现相关推荐

  1. Android 8.0学习(31)---Android 8.0 中的 ART 功能改进

    Android 8.0 中的 ART 功能改进 在 Android 8.0 版本中,Android Runtime (ART) 有了极大改进.下面的列表总结了设备制造商可以在 ART 中获得的增强功能 ...

  2. Android 7.0中的多窗口-分屏-实现解析

    禁用APK分屏-AndroidManifest.xml添加属性:android:resizeableActivity="false" Android 7.0中的多窗口实现解析 标签 ...

  3. Android 5.0发布日期/机型/功能传言汇总

    根据目前已知的信息,可以肯定的其甜点代号为"酸橙派"(Key Lime Pie):同时,一台运行Android 5.0系统的索尼手机的性能测试截图也被曝光,加大了传闻的真实性.随着 ...

  4. C# 9.0中的新增功能 - 译

    https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9 What's new in C# 9.0 C# 9.0中的新增功能 ...

  5. Android studio3.0开启抓包功能打包会使apk体积增大好几倍

    Android studio3.0开启抓包功能打包会使apk体积增大好几倍 Android studio3.0开启抓包功能打包会使apk体积增大好几倍: 今天尝试了下AS3.0抓包功能,还没怎么整明白 ...

  6. .NET Core 3.0中的新功能和增强功能

    目录 介绍 主要变化 Windows桌面支持 本机可执行文件 JSON API 更好的垃​​圾收集器 性能改进 Docker增强 ARM64支持 物联网支持 密码学 与.NET Core 2.2的AP ...

  7. Android 各层中日志打印功能的应用

    Android 各层中日志打印功能的应用 1. HAL层 头文件:#include <utils/Log.h>  对应的级别 打印方法   VERBOSE LOGV() DEBUG LOG ...

  8. .Net 6.0中的新增特性_.Net 6.0中的新增功能

    .Net 6.0中的新增特性_.Net 6.0中的新增功能 一..Net 6 介绍 .NET 6 作为 LTS 长期支持版本,.NET 6 将会获得 3 年的技术支持. .NET 6 是首个原生支持 ...

  9. linux 电源管理 唤醒,Android 4.0 中的PowerManager(电源管理--休眠唤醒) [转]

    转发请注明出处: 最近,在学习让系统进入休眠的过程中,学习了电源管理的相关知识.为了备忘和理清思路现整理成文,以便日后查阅. 移动设备由于自身的限制有一个硬伤就是电量的消耗问题.由于电池的电量有限,所 ...

最新文章

  1. myeclipse中安装svn插件
  2. 隐秘的角落——一个CIO的惊魂72小时
  3. 段寄存器和8种地址寻址方式
  4. STM32开发 -- 时钟系统详解
  5. 中国式创新技术“步态识别”终于来临,你大胆地走两步,我就知道你是谁
  6. datetimepicker 更新值无效_文献阅读之Voronoi图的生成与更新
  7. [洛谷P3228] [HNOI2013]数列
  8. jquery ajax缓存问题解决方法小结
  9. python拦截修改数据包_拦截指定数据、修改JS -- mitmproxy
  10. C# RabbitMQ(一)介绍
  11. 从网站建设看中外差异
  12. JADE(Java Agent Development Framework)笔记
  13. 学习方法推荐——番茄工作法
  14. 红旗linux桌面版_瑞星ESM杀毒软件For Linux获红旗兼容性认证
  15. word流程图变为图片格式_图片如何转换成word?新手小白几步就学会了
  16. python爬虫 提取豆瓣Top250电影信息
  17. ICQ官方中文版 v10.0.12161.0
  18. C#服务端的微信小游戏——多人在线角色扮演(六)
  19. 夺命雷公狗---DEDECMS----32dedecms电影网评价星星功能的实现
  20. 1108 String复读机 – PAT乙级真题

热门文章

  1. Dataset之MNIST:自定义函数mnist.load_mnist根据网址下载mnist数据集(四个ubyte.gz格式数据集文件)
  2. Py之argparse:Python库之argparse(命令行解析)简介、安装、使用方法之详细攻略
  3. HTTP:HTTP的server and client详细攻略
  4. 2019-05-30启动redis 后台服务运行·
  5. leetcode347 - Top K Frequent Elements - medium
  6. oracle发送邮件
  7. duilib 自带树形控件的认识
  8. FlashInspector 【Firefox浏览器插件,flash分析工具】
  9. 收藏开发人员常去网站
  10. WMS Schema