Android FDE 加密过程

10 篇文章 0 订阅
1 篇文章 0 订阅

Android Full Disk Encryption

FDE是android设备全盘加密的简称;主要用于对Android设备userdata分区数据的加密,以实现数据保护的目的

  1. FDE是什么
    • FDE是Full Disk Encrypt的缩写
      • 保护/data 分区数据
      • 参考google的官方介绍(需要访问google网络) encryption
      • 一种保护user data的机制。例如:联系人,图片,视频等等
      • 截止当前,Android支持加密userdata分区的数据以及不可移除sdcard的数据
      • 不支持其他分区的加密
    • FDE如何工作
      • 基于android kernel的dm-crypt feature 实现
      • 128 Advanced Encryption Standard(AES) with cipher-block chaining(CBD)and ESSIV:SHA256
      • Android5.0之后,首次开机会加密
      • 加密之后,将/data挂在到dm的虚拟节点上
  2. 首次启动
    • fstab.qcom 标志
      • encryptable manually encrypt
      • foeceencrypted encrypt in fist boot
    • 使用默认的密码获得master key
      • 默认密码是”default_password”
      • Keymaster(HW/SW) general key blob,存储在footer中
      • default_password_salt(random)–>scrypt–>derived key
      • Derived+keypair–>sign–>intermedia key(ikey)
      • salt,keybob,derived key stored in footer
      • set ikey to TZ
    • Hardware Crypto
      • TZ generates an encrypt key derived from new password,set to crypto enginee
    • Software Crypto
      • Call APU to set key with ikey(security_hw.c)
  3. 非首次开机
    • 如果用户设置了pin/或者密码
      • 弹出提示用于输入密码
    • 使用默认的密码获得master key
      • 从footer中读取salt,keyblob,derived key
      • 从输入或者salt中获得derivedkey,并进行比较
      • Derived+keypair–>sign–>intermedia key(ikey)
      • salt,keybob,derived key stored in footer
      • set ikey to TZ
    • 升级相关
      • 如果使用了软件加密,可以直接软件升级
      • 如果使用了硬件加密,不可以直接软件升级
  4. init.rc
    • on property:vold.decrypt=trigger_default_encryption
      • start defaultcrypto
    • service defaultcrypto /system/bin/vdc –wait cryptfs mountdefaultencrypted
      • disable
      • oneshot
      • #vold will set vold.decrypt to trigger_restart_framework(default encryption) or trigger_restart_min_framework(other encryption)
  5. FDE 启动总结
    • init 进程读取fstab中,并依据fstab中的配置进行挂载
    • 在挂载过程中发现该分区标记了encrypted,使用property触发一个属性
    • vdc服务传递命令给cryptd socket
    • 监听器接收到该socket,然后触发encrypt/decrypt 动作
    • 重要的函数
      • CryptCommandListener::CryptfsCmd::runCommand
      • get_keymaster_hw_fde_password
      • set_hw_device_encryption_key
      • create_crypto_blk_dev
      • update_hw_device_encryption_key

Settings中加密入口

//package/apps/Settings
CryptKeeper.java
CryptKeeperSettings.java
CryptKeeperConfirm.java

调试加密过程显示的UI

//正常显示进度条
adb shell pm enable com.android.settings/.CryptKeeper
adb shell am start  -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "progress"  -n com.android.settings/.CryptKeeper
//提示输入密码
adb shell am start  -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "password"  -n com.android.settings/.CryptKeeper
//出现错误
adb shell am start  -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "error"  -n com.android.settings/.CryptKeeper

调用关系

mountservice
MountService 启动后,会创建两个Thread 一个用于处理同底层vold的交互的、一个用于处理加密 。启动NativeDaemonConnector Thread 之后,对socket进行监听
调用流程:

NativeDaemonConnector.run() 
public int encryptStorage(int type, String password) 
-->mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", …);
    --> CryptCommandListener::CryptfsCmd::runCommand(SocketClient*, argc, **argv)
        -->cryptfs_enable_default(char *howarg, int allow_reboot)
            -->cryptfs_enable_internal(char*,int,char*,int)
Created with Raphaël 2.1.0 encryptStorage encryptStorage mCryptConnector.execute mCryptConnector.execute CryptCommandListener_CryptfsCmd_runCommand CryptCommandListener_CryptfsCmd_runCommand cryptfs cryptfs 加密 socket:CryptCommandListener::CryptfsCmd::runCommand
主要代码解释
int CryptCommandListener::CryptfsCmd::runCommand(SocketClient *cli,
                                             int argc, char **argv) {
} else if (!strcmp(argv[1], "cryptocomplete")) {
        int type = getType(argv[3]);
        if (type == -1) {
        } else if (type == CRYPT_TYPE_DEFAULT) {
          rc = cryptfs_enable_default(argv[2],/*allow_reboot*/false);
        } else {
            rc = cryptfs_enable(argv[2], type, argv[4],
                                /*allow_reboot*/false);
        }
    } else if (!strcmp(argv[1], "changepw")) {
}
//比较文件系统的和分区的大小
int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
                            int allow_reboot)
    /* Get the size of the real block device */
    int fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);
    get_blkdev_size(fd, &nr_sec);
        fs_size_sec = get_fs_size(real_blkdev);
        if (fs_size_sec > max_fs_size_sec) {
            SLOGE("Orig filesystem overlaps crypto footer region.  Cannot encrypt in place.");
/*获得一个随机的密钥对和盐,作为加密的密码。
将密钥对和盐存储在data 分区的footer中*/
static int create_encrypted_random_key(char *passwd, unsigned char *master_key, unsigned char *salt,crypt_mnt_ftr *crypt_ftr) {
    /* Get some random bits for a key */
    fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
    read(fd, key_buf, sizeof(key_buf));
    read(fd, salt, SALT_LEN);
    close(fd);
    /* Now encrypt it with the password */
    return encrypt_master_key(passwd, salt, key_buf, master_key, crypt_ftr);
}
/* Create mapped block device /dev/dm-0 with key_index which retrieved from set_hw_device_encryption_key
*/
decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev, "userdata");
rc = cryptfs_enable_all_volumes(&crypt_ftr, how, crypto_blkdev, real_blkdev, previously_encrypted_upto);
cryptfs_enable_all_volumes
--->cryptfs_enable_inplace 
------> cryptfs_enable_inplace_ext4
//加密进程从真实的设备节点读取数据并将其写入到mapper的设备节点中
static int cryptfs_enable_inplace_ext4(char *crypto_blkdev,
                                       char *real_blkdev,
                                       off64_t size,
                                       off64_t *size_already_done,
                                       off64_t tot_size,
                                       off64_t previously_encrypted_upto)
{    encrypt_groups(&data);}

加密实现

//判定当前设备是否正在被加密或者曾经加密过程被中断了。
if (how == CRYPTO_ENABLE_INPLACE
      && get_crypt_ftr_and_key(&crypt_ftr) == 0
      && (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS)) {
    previously_encrypted_upto = crypt_ftr.encrypted_upto;
    crypt_ftr.encrypted_upto = 0;
    crypt_ftr.flags &= ~CRYPT_ENCRYPTION_IN_PROGRESS;
    //加密的状态是一直不确定的。直到加密完成,然后重启。所以,这里首先将flag设置为CRYPT_INCONSISTENT_STATE。
    //待加密完成之后,将CRYPT_INCONSISTENT_STATE标志移除
    crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
    put_crypt_ftr_and_key(&crypt_ftr);
}
//判断当前加密状态,通过ro.crypto.state属性来感知
//计算real block device的大小
//申请一个wakelock,这里可以根据需求申请partial或者full wake lock
snprintf(lockid, sizeof(lockid), "enablecrypto%d", (int) getpid());
acquire_wake_lock(PARTIAL_WAKE_LOCK, lockid);
/* The init files are setup to stop the class main and late start when
 * vold sets trigger_shutdown_framework.
 */
//设置vold.decrypt属性的值为trigger_shutdown_framework,触发init进程shutdown class main
property_set("vold.decrypt", "trigger_shutdown_framework");
SLOGD("Just asked init to shut down class main\n");
/* Ask vold to unmount all devices that it manages */
//unmount 被管理的所有设备
if (vold_unmountAll()) {
    SLOGE("Failed to unmount all vold managed devices");
}

/* Now unmount the /data partition. */
//挂在data分区
if (wait_and_unmount(DATA_MNT_POINT, false)) {
    if (allow_reboot) {
        goto error_shutting_down;
    } else {
        goto error_unencrypted;
    }
}

//为了提升体验,这里所做的一些额外工作
/* Do extra work for a better UX when doing the long inplace encryption */
if (how == CRYPTO_ENABLE_INPLACE) {
    ///data分区已经被卸载了,这里需要将tmpfs挂在到/data并且设置一个系统属性告诉系统,我们要开始加密啦。。
    if (fs_mgr_do_tmpfs_mount(DATA_MNT_POINT)) {
        goto error_shutting_down;
    }else {
         SLOGD("Successful: mount tmpfs for '%s'\n", DATA_MNT_POINT);
         //..
    }
    //告诉framework,我们要开始加密啦。framework
    property_set("vold.encrypt_progress", "0");

//在/data上座一些必要的准备。
//设置vold.decryp=trigger_post_fs_data来触发系列动作。
    //动作完成之后,会将vold.post_fs_data_done设置为1,这里等待50s
    /*
     *property_set("vold.post_fs_data_done", "0");
     *property_set("vold.decrypt", "trigger_post_fs_data");
     *SLOGD("Just triggered post_fs_data\n");
    */
    if (prep_data_fs()) {
        goto error_shutting_down;
    }
     //等待2s,shutting down framework并不是同步进行。
//故,需要等待一段时间。否则在某些设备上,图形相关的服务有有问题。
    sleep(2);
}

/* Start the actual work of making an encrypted filesystem */
/* Initialize a crypt_mnt_ftr for the partition */
//开始真正的加密前的初始化。首先初始化得到crypt_mnt_ftr.
if (previously_encrypted_upto == 0) {
    if (cryptfs_init_crypt_mnt_ftr(&crypt_ftr)) {
        goto error_shutting_down;
    }

    if (!strcmp(key_loc, KEY_IN_FOOTER)) {
        crypt_ftr.fs_size = nr_sec
          - (CRYPT_FOOTER_OFFSET / CRYPT_SECTOR_SIZE);
    } else {
        crypt_ftr.fs_size = nr_sec;
    }

    //加密的状态是一直不确定的。直到加密完成,然后重启。所以,这里首先将flag设置为CRYPT_INCONSISTENT_STATE。
    //待加密完成之后,将CRYPT_INCONSISTENT_STATE标志移除
    crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
    crypt_ftr.crypt_type = crypt_type;
    //选择不同的加密方式 "aes-xts"(使用软件加密) "aes-cbc-essiv:sha256"(使用硬件加密)
    //设置秘钥,将秘钥存储在分区的尾部。
#ifndef CONFIG_HW_DISK_ENCRYPTION
    strlcpy((char *)crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256", MAX_CRYPTO_TYPE_NAME_LEN);
#else
    strlcpy((char *)crypt_ftr.crypto_type_name, "aes-xts", MAX_CRYPTO_TYPE_NAME_LEN);
    rc = clear_hw_device_encryption_key();
    rc = set_hw_device_encryption_key(passwd,

#endif

    /* Make an encrypted master key */
    if (create_encrypted_random_key(passwd, crypt_ftr.master_key, crypt_ftr.salt, &crypt_ftr)) {
        SLOGE("Cannot create encrypted master key\n");
        goto error_shutting_down;
    }

    //将key写入到分区结尾
    put_crypt_ftr_and_key(&crypt_ftr);

}

if (how == CRYPTO_ENABLE_INPLACE) {
    /* startup service classes main and late_start */
    //启动services class main以及late_start
    property_set("vold.decrypt", "trigger_restart_min_framework");
    SLOGD("Just triggered restart_min_framework\n");
    //framework正在重启。稍等就可以看到进度条的显示了。
    //进度条显示的含义是:正在加密的进度或者加密映射的文件进度
}

//创建master key
//将‘userdata’映射到加密节点上
decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev,
                      "userdata");

/* If we are continuing, check checksums match */
//读取的sha256值,同计算得到的sha256值进行比较
rc = 0;
if (previously_encrypted_upto) {
    __le8 hash_first_block[SHA256_DIGEST_LENGTH];
    rc = cryptfs_SHA256_fileblock(crypto_blkdev, hash_first_block);

    if (!rc && memcmp(hash_first_block, crypt_ftr.hash_first_block,
                      sizeof(hash_first_block)) != 0) {
        SLOGE("Checksums do not match - trigger wipe");
        rc = -1;
    }
}

if (!rc) {
    rc = cryptfs_enable_all_volumes(&crypt_ftr, how,
                                    crypto_blkdev, real_blkdev,
                                    previously_encrypted_upto);
}

/* Calculate checksum if we are not finished */
if (!rc && how == CRYPTO_ENABLE_INPLACE
        && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {
    rc = cryptfs_SHA256_fileblock(crypto_blkdev,
                                  crypt_ftr.hash_first_block);
    if (rc) {
        SLOGE("Error calculating checksum for continuing encryption");
        rc = -1;
    }
}

/* Undo the dm-crypt mapping whether we succeed or not */
delete_crypto_blk_dev("userdata");

if (! rc) {//条件判断ok
    /* Success */
    crypt_ftr.flags &= ~CRYPT_INCONSISTENT_STATE;

    if (how == CRYPTO_ENABLE_INPLACE
          && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {
        SLOGD("Encrypted up to sector %lld - will continue after reboot",
              crypt_ftr.encrypted_upto);
        crypt_ftr.flags |= CRYPT_ENCRYPTION_IN_PROGRESS;
    }

    put_crypt_ftr_and_key(&crypt_ftr);

#ifdef MTK_EMMC_SUPPORT
    {
        struct phone_encrypt_state ps;
        ps.state = PHONE_ENCRYPTED;
        if (misc_set_phone_encrypt_state(&ps, fstab) < 0) {
            SLOGE("Failed to set encrypted status to 0x%x in MISC\n", ps.state);
        }
        else {
            SLOGD("Success: Set encrypted status to 0x%x in MISC\n", ps.state);
        }
    }
#endif

    //判断ro.crypted.state的值是否为encrypted(代表加密成功)
    if (how == CRYPTO_ENABLE_WIPE
          || crypt_ftr.encrypted_upto == crypt_ftr.fs_size) {
      char value[PROPERTY_VALUE_MAX];
      property_get("ro.crypto.state", value, "");
      if (!strcmp(value, "")) {
        /* default encryption - continue first boot sequence */
        property_set("ro.crypto.state", "encrypted");
        release_wake_lock(lockid);
        cryptfs_check_passwd(DEFAULT_PASSWORD);
        property_set("vold.encrypt_progress", "");
        cryptfs_restart_internal(1);
        return 0;
      } else {
        sleep(2); /* Give the UI a chance to show 100% progress */
        cryptfs_reboot(reboot);
      }
    } else {
        sleep(2); /* Partially encrypted, ensure writes flushed to ssd */
        cryptfs_reboot(shutdown);
    }
} else {//初始条件判断失败。
    char value[PROPERTY_VALUE_MAX];

    property_get("ro.vold.wipe_on_crypt_fail", value, "0");
    if (!strcmp(value, "1")) {
        /* wipe data if encryption failed */
        SLOGE("encryption failed - rebooting into recovery to wipe data\n");
        mkdir("/cache/recovery", 0700);
        int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
        if (fd >= 0) {
            write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);
            write(fd, "--reason=cryptfs_enable_internal\n", strlen("--reason=cryptfs_enable_internal\n") + 1);
            close(fd);
        } else {
            SLOGE("could not open /cache/recovery/command\n");
        }
        cryptfs_reboot(recovery);
    } else {
        /* set property to trigger dialog */
        property_set("vold.encrypt_progress", "error_partially_encrypted");
        release_wake_lock(lockid);
    }
    return -1;
}

参考

Vold properties


PropertyDescription
vold.decrypt=trigger_encryptionEncrypt the drive with no password.
vold.decrypt=trigger_default_encryptionCheck the drive to see if it is encrypted with no password. If it is,decrypt and mount it, else setvold.decrypt to trigger_restart_min_framework.
vold.decrypt=trigger_reset_mainSet by vold to shutdown the UI asking for the disk password.
vold.decrypt=trigger_post_fs_dataSet by vold to prep /data with necessary directories, et al.
vold.decrypt=trigger_restart_frameworkSet by vold to start the real framework and all services.
vold.decrypt=trigger_shutdown_frameworkSet by vold to shutdown the full framework to start encryption.
vold.decrypt=trigger_restart_min_frameworkSet by vold to start the progress bar UI for encryption or prompt for password, depending on the value of ro.crypto.state.
vold.encrypt_progressWhen the framework starts up, if this property is set, enter the progress bar UI mode.
vold.encrypt_progress0 to 100 The progress bar UI should display the percentage value set.
vold.encrypt_progress=error_partially_encryptedThe progress bar UI should display a message that the encryption failed, and give the user an option to factory reset the device.
vold.encrypt_progress=error_reboot_failedThe progress bar UI should display a message saying encryption completed, and give the user a button to reboot the device. This error is not expected to happen.
vold.encrypt_progress=error_not_encryptedThe progress bar UI should display a message saying an error occurred, no data was encrypted or lost, and give the user a button to reboot the system.
vold.encrypt_progress=error_shutting_downThe progress bar UI is not running, so it is unclear who will respond to this error. And it should never happen anyway.
vold.post_fs_data_done0 Set by vold just before setting vold.decrypt to trigger_post_fs_data.
vold.post_fs_data_done1 Set by init.rc or init.rc just after finishing the task post-fs-data.

init properties

PropertyDescription
ro.crypto.fs_crypto_blkdevSet by the vold command checkpw for later use by the vold command restart.
ro.crypto.state unencryptedSet by init to say this system is running with an unencrypted /data ro.crypto.state encrypted. Set by init to say this system is running with an encrypted /data.
ro.crypto.fs_type
ro.crypto.fs_real_blkdev
ro.crypto.fs_mnt_point
ro.crypto.fs_options
ro.crypto.fs_flagsThese five properties are set by init when it tries to mount /data with parameters passed in from init.rc. vold uses these to setup the crypto mapping.
ro.crypto.tmpfs_optionsSet by init.rc with the options init should use when mounting the tmpfs /data filesystem

init actions

on post-fs-data
on nonencrypted
on property:vold.decrypt=trigger_reset_main
on property:vold.decrypt=trigger_post_fs_data
on property:vold.decrypt=trigger_restart_min_framework
on property:vold.decrypt=trigger_restart_framework
on property:vold.decrypt=trigger_shutdown_framework
on property:vold.decrypt=trigger_encryption
on property:vold.decrypt=trigger_default_encryption

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值