【输出文档】 Android 加密 模块源码分析

 

 

 

 

                           Android6.0 加密模块解析

 

 

前言:

加密流程就是用一个加密了的key对Android 系统中的用户数据进行加密。一旦设备被加密了,所有用户创建的数据在提交到磁盘前都会被自动加密,并且在返回到调用的进程前,所有的用户数据会被自动解密。

   Android 的disk机密是基于dm-crypt的,dm-crypt是kernl上运行在block 设备层的一个加密工具。因此,加密实际操作对象是对嵌入式多媒体存储设备(eMMC)和类似的闪存设备中保存的数据信息。加密是不可能基于YAFFS工具的,因为YAFFS是直接与NAND闪存芯片进行通信的。

  1. 加密类型

加密流程对于一个设备来说,有可能出现4中不同的情况,但是一般情况下,都是对设备进行一次加密,然后再进行一个正常的开机流程:

    ●加密一个之前没有加密的设备:

      • 在fstab配置信息中标注了forceencrypt= true,第一次开机时会强制性进行加密;

      • 在fstab配置信息中没有标注 forceencrypt= true,第一次开机不会强制性进行加密,需要用户开机后手动触发加密流程。

 

    ●一个已经加密设备的开机:

  • 在无密码状态下对已加密的设备进行开机

  • 在有密码需求的状态下对加密设备的开机

针对以上4中情况,我们把加密具体分为以下4中具体的流程:

 

1.1 对强制要求加密的设备进行加密

 

   这是从Android 5.0 开始,google原生机默认的开机模式,即在google原生机的fstab配置文件中,/data分区都是标注了forceencrypt = true 信息的。

1.1.1 检查forceencrypt 标志位

init 先按照fstab的配置信息检查 forceencrypt 配置信息,需要加密,init Unmount /data.

1.1.2 加密/data

init服务会设置vold.decrypt = "trigger_encryption",这个属性的变化触发init 挂载fstab文件中的分区配置时返回“FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION”值,触发vold.decrypt = trigger_encryption,init 启动surfaceflinger 和encrypt服务。

 

on property:vold.decrypt=trigger_encryption

    start surfaceflinger

    start encrypt

 

 

1.1.3 mount tmpfs

vold服务将挂载一个临时的“/data”,将记录加密进度的系统属性vold.encrypt_progress 赋值为“0”。vold 挂载这个tmpfs data 的作用在于

1.2 对已经正常开机的设备进行加密

对一个未加密的设备进行加密

这个流程就是由用户触发的,并且将指向“inplace encryption”这个流程,这时UI 会要求手机电池是充满状态而且USB 线是连接状态。

注:加密流程中如果断电,必须恢复出厂设置后才能继续使用

使能“inplace encryption”,vold会启动一个loop来读取real block device 的每个sector并且将它写入crypto block device。vold 会在读写sector之前先检查它是否有被调用,这个让encryption在左右一点数据记录的新设备上比较迅速。

 

设备状态相关:设置 ro.crypto.state = “unencrypted” 并且执行init中“on nonencrypted ”触发的流程

 

1.2.1.检查password:

  UI会通过"cryptfs enable inplace"这个命令来要求输入锁屏的password来开始流程;

 

1.2.2.关闭framework:

  vold会检查errors,返回 -1 就表示无法加密并且打印相应log。如果可以加密,将会设置 系统属性 “vold.decrypt =trigger_shutdown_framework.”,这将会导致init.rc 关闭 late_started 和main 类的services.

 

1.2.3.卸载 /data分区:

  vold 会卸载“ mnt/sdcard” 和“/data” 分区

 

1.2.4.开始加密 /data分区

  vold 会开启crupto mapping,这会创建一个虚拟的crypto block device,它会在真实的block device 上进行“map”并且会在write时进行加密,read时进行解密。vold将会创建并写出 crypto metadata.

 

1.2.5.在加密过程中,挂载tmpfs:

  vold 会在/data分区挂载一个tmpfs(挂载时是option 属性通过ro.crpto.tmpfs_option来确定)。然后设置属性“vold.encrypt_progress = 0”。vold 将/data这个tmpfs用于启动一个加密了的system并且设置系统属性 vold.decrypt = trigger_restart_min_fraemwork.

 

1.2.6.启动framework来展示加密进度

trigger_restart_min_framework 会唤起init.rc来start main class services.当famework监听到vold.encrypt_progress = 0时,它会启动显示进度条的UI,它每隔5秒钟就检查这个系统属性并且跟新进度条。encryptiom loop每次加密完成其他的partition后,它都会更新vold.encrypt_progress这个属性。

 

1.2.7.当/data加密完成,重启

当/data分区加密成功,vold会清除metadata中的ENCRYPTION_IN_PROGRESS这个flags,并且重启system

如果重启失败,vold会设置属性vold.encrypt_progress = error_failed 并且在UI 中展示一条信息,要求用户重启。

 

1.3 已加密的设备有密码状态下的开机

这种情况指的是: 你启动的是一个设置了密码的加密系统,这个系统的加密密码 可能是一个pin ,pattern 或者密码

1.3.1.检测加密设备的密码
通过判断标志位 ro.crypto.state = "encrypted"来确定设备已经被加密了
vold 会设置 “vold.decrypt = trigger_restart_min_framework”,因为 /data 已经被一个密码加密了

1.3.2.挂载tmpfs
init.rc中传递下来的关于/data 挂载的options 被init进程用5个系统属性保存下来。vold使用这些系统属性来启动加密流程:
  1.ro.crypto.fs_type
  2.ro.crypto.fs_real_blkdev
  3.ro.crypto.fs_mnt_point
  4.ro.crypto.fs_options
  5.ro.crypto.fs_flags

1.2.3.启动framework来要求输入password
framework启动后会监听vold.decrypt = trigger_restart_min_framework.此时framerwork就知道现在是正在启动一个tmpfs /data并且它需要获取用户的password。
然而,首先我们必须确定disk已经被正确的加密了。我们会向vold发送命令“cryptfs cryptocomplete ”,如果加密成功,vold会返回0;如果出现错误,会返回 -1;如果加密没有完全完成,会返回-2.
vold是通过检查crypto metadata的CRYPTO_ENCRYPTION_IN_PROCESS标志位。如果crypto metadata中的这个标志位被设定了,加密的流程就被打断,并且设备上就不再有可用的数据了。如果vold返回一
个错误,UI会要求用户重启并对设备恢复出厂设置。

1.2.4.获取password后对data分区进行解密
  一旦cryptfs crytocomplete 成功了,framework会展示一个UI 界面来请求disk的password。UI 会将用户传入的password通过下发cryptfs checkpw命令到vold来检查。如果password是正确的(检查password正确的方法是可以成功的在一个暂时的区域挂载加密了的/data并且可以卸载它),vold将会保存加密了的块设备的名称在属性ro.crypto.fs_crypto_blkdev中并且返回0 到UI 进程,如果passworf是错误的,返回-1 到UI 进程。
 
1.2.5.关闭framework
  UI将会启动一个crypto boot graphic 并且通过cryptfs restart.vold 这个命令来设置系统属性 vold.decrypt = trigger_restart_min_main,这导致init.rc来启动class_reset main.这会停止所有的main类的service,而这些service保证了/data 这个tmpf处于未挂载状态。
 
1.2.6. 挂载/data分区
  vold 会挂载已经加密了的/data 分区并且准备新的分区,如果这个分区是按照wipe命令进行加密的,这个分区可能未初始化好的。然后会修改系统属性 vold.post_fs_data_done = 0 然后设置vold.decrypt = trigger_post_fs_data.这导致init.rc来运行post-fs-data命令。它们会创建必要的路径或者链接然后设置vold.post_fs_data_done = 1.一旦vold 监听到这个属性等于1,vold将设置系统属性 vold.decrypt = trigger_restart_framework.这导致init.rc重新启动main 类的service并且同时启动lte_start类的services。
1.2.7.启动整个framework

1.4 已加密的设备无密码状态下的开机

与1.3 相同,唯一的区别在于启动framework层后不会初始化输入密码的界面。

  1. 具体模块解析

流程图:

  1. 机密流程

3.1 do_mount_all 分析

3.1.1 do_mount_all 代码

init 会调用fs_mgr中do_mount_all 解析fstab文件,并对其中的配置信息进行挂载:

 

system/core/init/builtins.cpp

 

int do_mount_all(int nargs, char **args)

{

... ...

//fork 一个子进程,执行mount

    pid = fork();

    if (pid > 0) {

        /* Parent.  Wait for the child to return */

        int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));

... ...

ret = WEXITSTATUS(status);

... ...

    } else if (pid == 0) {

//子进程,args[1]参数就是”/fstab.flo”,fs_mgr_read_fstab将解析fstab文件

        fstab = fs_mgr_read_fstab(args[1]);

        child_ret = fs_mgr_mount_all(fstab);

        fs_mgr_free_fstab(fstab);

... ...

    }

if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {

    //fstab 中设置了默认强制加密

        property_set("vold.decrypt", "trigger_encryption");

} else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {

    //设备已经加密

        property_set("ro.crypto.state", "encrypted");

        property_set("ro.crypto.type", "block");

        property_set("vold.decrypt", "trigger_default_encryption");

} else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {

    //设备未加密,什么都不用处理

        property_set("ro.crypto.state", "unencrypted");

        /* If fs_mgr determined this is an unencrypted device, then trigger

         * that action.

         */

        action_for_each_trigger("nonencrypted", action_add_queue_tail);

    } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {

... ...

    } else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) {

... ...

    } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {

... ...

    } else if (ret > 0) {

        ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);

    }

}

 

● do_mount_all 的作用就是:

   •系统第一次开机,而且系统设置为强制加密,就走FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION

   •系统机密后开机,发现设备加密了,FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED

   •系统正常开机,不用加密,

FS_MGR_MNTALL_DEV_NOT_ENCRYPTED

 

3.1.2 fs_mgr_mount_all

system/core/fs_mgr/fs_mgr.c

 

int fs_mgr_mount_all(struct fstab *fstab)

{

... ...

    int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;

... ...

for (i = 0; i < fstab->num_entries; i++) {

... ...

        mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx);

... ...

//如果设备已机密,mret = -1/data 分区是无法正常挂载的

//如果设备未加密,正常情况下,mret = 0

        if (!mret) {

// 1.可以正常挂载,并且已经正常挂载了,这里就需要考虑是否是第一次开机,是否需要强制加密

            int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]);

... ...

            if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {

                encryptable = status;

            }

            continue;

        }

// 2.未挂载成功,判断设备是否已经加密了,这种情况下,无法通过mount挂载

        bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);

        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&

            fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {

... ...

        }

        if (mret && mount_errno != EBUSY && mount_errno != EACCES &&

            fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {

... ...

         //已判断设备已加密,在/data 分区下挂载tmpfs设备,而不是指定的实际设备

         if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) {

... ...

            continue;

         } 

         encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;

        }

... ...

    }

... ...

}

 

●fs_mgr_mount_all 的流程

  •先mount fstab中的各个项。如果成功的话,表明对应项的块设备肯定没有被加密(加密的设备没法被mount)

  •如果是强制加密,则先卸载这个设备,然后设置返回值FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION

  •如果mount失败,则需要判断是不是因为加密设备导致。如果该设备没有被清空(wiped),则先把tmpfs挂载到/data目录下。然后设置返回值为FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED

 

3.1.3 handle_encryptable

static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)

{

//如果设备是可加密的或者是强制加密的,那么需要引导加密流程     

    if (   (rec->fs_mgr_flags & MF_FORCECRYPT)

        || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {

        if (umount(rec->mount_point) == 0) {

            return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;

        } else {

            return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;

        }

    }

    //处理文件级别的加密

     if (rec->fs_mgr_flags & MF_FILEENCRYPTION)

     {

      //忽略

     }

     return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;

}

 

挂载tmpfs/data/目录下的原因:

    因为没有这个/data目录,系统根本没办法起来。因为系统很多运行时保存的信息,app自己的缓存目录等都放在/data下。为了保证系统能启动,所以这里先弄一个临时/data目录。等到后续把加密设备通过device-mapper方式准备好后,我们再把加密设备挂载到/data/目录下。注意,加密设备的mount不是直接挂载加密设备,而是通过挂载一个device-mapper来实现的。

3.1.4 处理mount结果

●如果是强制设备第一次开机,设置“vold.decrypt”属性值为“trigger_encryption”。

●如果是已经加密设备的后续开机,设置“ro.crypto.state”属性值为“encrypted”,同时设置"vold.decrypt"属性值为"trigger_default_encryption"。

●非加密设备,设置"ro.crypto.state"值为"unencrypted"。

 

3.2 强制加密设备的第一次开机

do_mount_all 的执行结果为设置了vold.decrypt = trigger_encryption,会触发加密:

 

init.rc:

on property:vold.decrypt=trigger_encryption

    start surfaceflinger

start encrypt

 

# One shot invocation to encrypt unencrypted volumes

service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default

    disabled

    oneshot

    # vold will set vold.decrypt to trigger_restart_framework (default

# encryption)

 

service surfaceflinger /system/bin/surfaceflinger

    class core

    user system

    group graphics drmrpc

    onrestart restart zygote

    writepid /dev/cpuset/system-background/tasks

 

强制加密设备的首次加密将由vdc来处理。vdc其实不过是给vold发送enablecrypto命令罢了,真正的加密工作是由vold来完成的。

vold 进行加密的流程后续分析。

 

3.3 已加密设备的开机

init.rc

on property:vold.decrypt=trigger_default_encryption

start defaultcrypto

# One shot invocation to deal with encrypted volume.

service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted

    disabled

    oneshot

    # vold will set vold.decrypt to trigger_restart_framework (default

# encryption) or trigger_restart_min_framework (other encryption)

 

也是最后有vold来完成的,后续再分析vold的加密流程。

3.4 正常开机后主动进行加密

3.4.1 Settings 流程

用户通过在Settings应用“安全”-->“加密手机”开启加密流程,因为加密耗时,Settings会需求手机连接USB线并且手机电池处于100%状态。

     选择加密

3.4.1.1 CryptKeeperSettings

CryptKeeperSettings.java::onCreateView

mInitiateButton = (Button) mContentView.findViewById(R.id.initiate_encrypt);

即为加密按钮点击加密后触发:

private Button.OnClickListener mInitiateListener = new Button.OnClickListener() {

   public void onClick(View v) {

     if (!runKeyguardConfirmation(KEYGUARD_REQUEST))

   }

 

 private boolean runKeyguardConfirmation(int request) {

   Resources res = getActivity().getResources();

   ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(getActivity(), this);

    //DevicePolicyManager那获取设备的密码控制要求,如果没有指定的话,将直接进入

   if (helper.utils().getKeyguardStoredPasswordQuality(UserHandle.myUserId())

                == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {

      //一般是没有设置密码控制级别的,所以加密的时候将使用默认加密,密码为””

      showFinalConfirmation(StorageManager.CRYPT_TYPE_DEFAULT, "");

      return true;

    }

      return helper.launchConfirmationActivity(request,

                res.getText(R.string.crypt_keeper_encrypt_title), true);

  }

 

  从Android 5.1 开始,系统支持一种默认的加密方式,即加密默认用户传入的密码是“default_password”。这是在用户没有设置解锁方法的情况下,系统默认的行为。如果有DevicePolicyManager或者用户自己设置了解锁密码,则系统会用它们做为设备加密密码。

 

CryptKeeperSettings.java::showFinalConfirmation

private void showFinalConfirmation(int type,String password) {

   Preference preference = new Preference(getActivity());

    //启动CrypteKeeperConfirm界面

   preference.setFragment(CryptKeeperConfirm.class.getName());

   preference.setTitle(R.string.crypt_keeper_confirm_title);

    //type为默认的加密方法,password为””

   preference.getExtras().putInt("type", type);

   preference.getExtras().putString("password", password);

   ((SettingsActivity) getActivity()).onPreferenceStartFragment(null,

         preference);

}

 

3.4.1.2CryptKeeperConfirm

CryptKeeperConfirm.java::mFinalClickListener

private Button.OnClickListener mFinalClickListener= new

                Button.OnClickListener() {

   publicvoid onClick(View v) {

        .....

        //启动一个Blank Activity,代码也在此文件中

       Intent intent = new Intent(getActivity(), Blank.class);

       intent.putExtras(getArguments());

       startActivity(intent);

 

        //2. The system locale.

        try{

        //调用MountServicesetField功能,设置系统语言

       IBinder service = ServiceManager.getService("mount");

       IMountService mountService = IMountService.Stub.asInterface(service);

       mountService.setField("SystemLocale",

                    Locale.getDefault().toLanguageTag());

        }......

    }

 };

 

CryptKeeperConfirm.java::Blank

    public static class Blank extends Activity {

        private Handler mHandler = new Handler();

 

        @Override

        public void onCreate(Bundle savedInstanceState) {

            setContentView(R.layout.crypt_keeper_blank);

 .. ... ...

             //禁止使用状态栏的功能,加密过程最好是不要被打搅!

            StatusBarManager sbm = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);

            sbm.disable(StatusBarManager.DISABLE_EXPAND

                    | StatusBarManager.DISABLE_NOTIFICATION_ICONS

                    | StatusBarManager.DISABLE_NOTIFICATION_ALERTS

                    | StatusBarManager.DISABLE_SYSTEM_INFO

                    | StatusBarManager.DISABLE_HOME

                    | StatusBarManager.DISABLE_SEARCH

                    | StatusBarManager.DISABLE_RECENT

                    | StatusBarManager.DISABLE_BACK);

... ...

            mHandler.postDelayed(new Runnable() {

                public void run() {

                    IBinder service = ServiceManager.getService("mount");

... ...

                   //调用MountServiceencrytStorage方法

                     mountService.encryptStorage(args.getInt("type", -1), args.getString("password"));

                    }

... ...

        }

    MountService encryptStorage 方法最后调用vold服务进行后续的加密流程,我们在后续里面分析。

 

     加密进度显示

●当vold在加密设备的过程中,这个时间可能很长很长,根据设备中之前是否有很多数据有关。

 

●另外,挂载加密后的设备时,可能用户用得不是默认密码,那这个时候需要启动framework,然后弹出一个密码输入框让用户输入密码。这也就是为什么加密设备挂载时会先mount一个tmpfs/data分区下。因为这个时候framework里那些service是要启动的,还有输入法等所谓的core app

 

在加密过程中或者加密设备挂载前,framework有一些特殊的处理:

 

SystemServer.java::startBootstrapServices

private void startBootstrapServices() {

... ...

StringcryptState = SystemProperties.get("vold.decrypt");

    // ENCRYPTING_STATE值为“trigger_restart_min_framework

    if(ENCRYPTING_STATE.equals(cryptState)) {

       mOnlyCore= true;

    } elseif (ENCRYPTED_STATE.equals(cryptState)) {

     // ENCRYPTED_STATE值为“1

       mOnlyCore = true;

    }

... ...

}

 

当属性vold.decrypt值为“trigger_restart_min_framework或者为”1的时候,mOnlyCoretruemOnlyCoretrue的时候,系统启动后只会解析在AndroidManifest.xml中标记coreApp = trueactivity

SettingsCryptKepper.java 会接受这个Home Intent。如下图:

             

为了防止在加密过程中用户乱动,状态栏和虚拟按键栏都没法使用了。

在加密前,还会判断是否应该让用户输入密码验证是否允许挂载加密设备。这个时候UI界面是:

               

 

对于挂载加密设备来说,系统其实并不是真的去解密存储设备,然后再挂载。而仅是判断用户输入的密码是不是对的。如果是对的话,它会通过device-mapper去挂载一个虚拟设备到/data下,而这个虚拟设备一方面连着实际的block设备。这样,当用户从/data读取、写入数据时,这个虚拟设备都会把数据进行加解密。比如用户读数据时,它会从真实设备里读取加密后的数据,然后解密返回给用户。用户写数据时候,它会加密后再写到真实设备中!

3.5 vold 加密流程

 

 

 

MountService中encryptStorage会将根据传入密码type直接传入vold中,这里,密码的type有如下几种:

    /// Consts to match the password types in cryptfs.h

    /** @hide */

    public static final int CRYPT_TYPE_PASSWORD = 0;

    /** @hide */

    public static final int CRYPT_TYPE_DEFAULT = 1;

    /** @hide */

    public static final int CRYPT_TYPE_PATTERN = 2;

    /** @hide */

public static final int CRYPT_TYPE_PIN = 3;

 

如果未设置密码,这对于的密码type为CRYPT_TYPE_DEFAULT。

 

enablecrypto对应的处理函数入口是cryptfs_enable(设置了密码)或者是cryptfs_enable_default(未设置密码)。它们内部都会调用cryptfs_enable_internal

3.5.1 cryptfs_enable_internal

/system/vold/cryptfs.c::cryptfs_enable_internal

 

 ●howarg :wipe 或者 inplace

          • wipe:表示先清干净存储设备,然后加密

          • inplace:对现有设备进行加密

 ●crypt_type :加密密码的种类,password ,pin 或者patter。没设置密码则为default。

 ●passwd : 加密密码,如果没有设置密码,则为“default_passwd”

 

int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,

                            int no_ui)

{

... ...

    if(!strcmp(howarg, "wipe")) {

      how =CRYPTO_ENABLE_WIPE;

    } elseif (! strcmp(howarg, "inplace")) {

      how = CRYPTO_ENABLE_INPLACE; //我们对应这种情况

}

... ...

    /*

     不知道什么原因,上一次加密还没处理完,系统就重启或者vold就重启了。为了处理这种情况,vold会把加密过程都写到一个地方去,然后要每次加密前都需要检查。因为一旦加密被干扰而又没正确处理的话,用户数据就会丢失,这是非常严重的事故!

    */

if (how == CRYPTO_ENABLE_INPLACE

          && get_crypt_ftr_and_key(&crypt_ftr) == 0

          && (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS)) {

 

}

 

... ...

}

 

     3.5.1.1 get_crypt_ftr_and_key

/system/vold/cryptfs.c::get_crypt_ftr_and_key

static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr) {

... ...

  /*

   get_crypt_ftr_info用于获取加密信息的存储位置。按Android的设计,这个信息可以存储在

   两个地方。一个是fstab里encryptable=xxxx中xxx这个存储设备里,也可以存储在

   需要挂载的设备里最后一段空间里。比如fstab.flo中,

   1 /dev/block/platform/msm_sdcc.1/by-name/userdata是要挂载的设备,所以加密信息

     存储在这个设备的最后一段空间里。但是由于我们还有下面这句话:

   2 encryptable=/dev/block/platform/msm_sdcc.1/by-name/metadata

     所以实际的加密信息存储在metadata文件里

  */

 

//fname:指明加密信息存储在哪个文件中

//starting_off:指明加密信息存储在文件的那个位置

  if (get_crypt_ftr_info(&fname, &starting_off)) {

    SLOGE("Unable to get crypt_ftr_info\n");

    return -1;

  }

... ...

//将加密信息保存在crypt_mnt_ftr 这个结构体中

  if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr))     

... ...

}

 

以上:

加密所用的上下文信息(包括加密版本号,密钥信息,加密方法,加密进度等)都是存储在某个地方。这个地方可以是加密设备最后一块区域,也可以单独指定一个文件(通过fstab encryptable=xxx来指定)。

 

我们继续分析cryptfs_enable_internel:

 

int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,

                            int no_ui)

{

 //确认是wipe数据还是对现有数据进行加密

 //过去加密密匙位置和其存储位置

 

 

  property_get("ro.crypto.state", encrypted_state, "");

  if(!strcmp(encrypted_state, "encrypted") &&!previously_encrypted_upto) {

     //previously_encrypted_upto表示上一次加密的进度,如果是0的话,而加密状态

       又是”encrypted”,就表示有问题

        goto error_unencrypted;

   }

   //取出key的存储路径和待加密的路径

   fs_mgr_get_crypt_info(fstab, key_loc, 0, sizeof(key_loc));

   fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));

   ... ...

       //打开真实设备并获取它的夸个数!

       fd =open(real_blkdev, O_RDONLY);

       nr_sec = get_blkdev_size(fd));

       close(fd);

       ... ...

       //加密耗电,所以要搞一个WakeLock

       acquire_wake_lock(PARTIAL_WAKE_LOCK, lockid);

       ... ...

       //设置init属性,vold.decrypt = trigger_shutdown_frameworkmainmlalate_start 类型的服务关闭,开始进行加密前,需要卸载/data分区,因为系统数据都保存在/data下面,需要先关闭主要服务来避免系统崩溃。

 

 

        property_set("vold.decrypt","trigger_shutdown_framework");

        //调用VolumeManager 卸载vold维护的所有的volume

        if (vold_unmountAll())

        ... ...

        //卸载 /data 分区

        if (wait_and_unmount(DATA_MNT_POINT, false))

        ... ...

        if (how == CRYPTO_ENABLE_INPLACE) {

          //inplace 代表队原数据进行加密

          //现在 /data分区已经被卸载了,我们就需要挂载一个临时替代的data分区,调用fs_mgr_do_tmpfs_mount 实现。

          if (fs_mgr_do_tmpfs_mount(DATA_MNT_POINT)) {

            goto error_shutting_down;

          }

          //用系统属性vold.encrypt_progress 来记录加密的进度

          property_set("vold.encrypt_progress", "0");

          if (prep_data_fs()) {

            // prep_data_fs的作用初始之前tmp_data分区挂载后,系统在tmp_data 区中创建系统运行的必要文件夹,为后续系统在tmp_data下与运行做准备。

            goto error_shutting_down;

          }

        }

    ... ...

}

 

     3.5.1.2 prep_data_fs

prep_data_fs() 的作用就是保证init中/data区中应该创建的文件都正常创建:

 

init 会在创建完所有的文件后,赋值vold.post_fs_data_done = 1

 

static int prep_data_fs(void)

{

    property_set("vold.post_fs_data_done", "0");

    property_set("vold.decrypt", "trigger_post_fs_data");

... ...

    for (i=0; i<DATA_PREP_TIMEOUT; i++) {

        char p[PROPERTY_VALUE_MAX];

 

        property_get("vold.post_fs_data_done", p, "0");

        //检查到属性值为1 ,确认data分区下文件创建完毕

        if (*p == '1') {

            break;

        } else {

            usleep(50000);

        }

... ...

}

 

 

继续分析crypfs_enable_internel

 

int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,

                            int no_ui)

{

     ... ... ...

     ... ... ...

      // 首次加密,进度为0

      if (previously_encrypted_upto == 0) {

        //初始化一个crypt_mnt_ftr结构。密钥大小是

         默认为16字节,和文件系统大小为0

         据推测,在最低限度,来电者将更新

         调用这个函数在文件系统的大小和crypto_type_name

        if (cryptfs_init_crypt_mnt_ftr(&crypt_ftr)) {

            goto error_shutting_down;

        }

        // 余下为具体的加密流程

        ... ... ... ...

        ... ... ... ...

        if (how == CRYPTO_ENABLE_INPLACE && !no_ui) {

           //这个动作会触发class main类型的service重启动,当然,zygote也就起来

            了然后framework都起来了。但是由于SystemServer只加载coreApp

          property_set("vold.decrypt", "trigger_restart_min_framework");

          ... ... ...

        }  

       

        //decrypt_master_key 比较复杂,大概的作用就是:

       利用device-mapper机制创建一个

      //1 device mapper设备,其实device mapper是一个驱动,叫mdmapped device之意)

      //2 为这个device mapper设备设置一个crypt虚拟项。这个crypt虚拟项会和

        待加密设备(real_blkdev)关联起来.

      //3 crypto_blkdev是上面device mapper创建的一个虚拟项设备,命名方式为

        /dev/block/dm-xxxxxxdevice mapperminor版本号。

 

        decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);

        //设备加解密工作就是在这个crypto_blkdev读写过程中完成的。以后我们挂载这

        crypto_blkdev/data分区就可以了。当从这个设备读的时候,它会从底层关

        联的 real_blkdev读取加密数据,然后解密传递给读者。当往这个设备写的时候,

        它会加密后再写到real_blkdev

 

        create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev,

                          "userdata");

        ... ...

        if (!rc) {

            //进行数据加密

            rc = cryptfs_enable_all_volumes(&crypt_ftr, how,crypto_blkdev, real_blkdev,

                                        previously_encrypted_upto);

        }

        ... ...

        // 删除crypto_blk设备

        delete_crypto_blk_dev("userdata");

        if (! rc) {

          // 加密成功了!!!!!

          ... ... ...

          if (how == CRYPTO_ENABLE_INPLACE

              && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {

            //如果加密完毕,encyrpted_upto将等于fs_size

         

            //接下来的流程为

            //ro.crypto.state的属性,这个属性:

            //1 如果值为空:表明是强制加密设备的第一次加密。那么我们要挂载这个已经加密的设备

            //2 如果值不为空(此时值应该是“uncrypted,在init中设置),表明我们是对以前没有加密的设备进行首次加密

            property_get("ro.crypto.state", value, "");

            if (!strcmp(value, "")) {

            //挂载加密设备,先检查默认密码是不是对的

            property_set("ro.crypto.state", "encrypted");

            cryptfs_check_passwd(DEFAULT_PASSWORD);

            cryptfs_restart_internal(1);

            return 0;

          }

         }

        }

      }

 

 

 去除具体加密的代码,以上即为加密的主要流程。

 

 

 

 

 

 

  1. 各种情况下的加密流程

4.1 cryptfs_enable_internal()函数时序图

各种情况下的加密流程,主要的加密动作都是在cryptfs_enable_internal函数中。

我们已用户在setting 中点击加密按钮进行加密开始分析:

 

 

总的来说,cryptfs_enable_internal函数中会设置 vold.decrypt = trigger_shutdown_framework,这会触发init中 class_reset late_start 和 class_reset main 此时,从用户的角度来看,手机处于黑屏状态。

 

 

 

 

在后面的流程中,cryptfs_enable_internal函数的主要作用是:

  1. 调用wait_and_unmount函数卸载原始的“data”分区;
  2. 调用fs_mgr_do_tmpfs_mount 函数挂载一个临时的data分区tmpfs(我们默认是用户设置了密码的加密——所谓了密码,就是用户设置的除了滑屏解锁以外解锁码等密码——后,系统进行加密时需求用户输入正确的密码才可以进行加密操作,如果没有设置除了滑屏解锁以外解锁码,则不会挂载临时的data分区tmpfs);
  3. 临时data分区挂载完成后,此时下发vold.decrypt=trigger_restart_min_framework,触发init 调用 class_start main :此时从用户的角度看,就是手机重启开机(但是没有4G界面);
  4. 开机后,会显示一个加密进度界面:

 

注:加密的进度是读取vold.encrypt_progress 这个属性值获取的,这个属性会根据加密的进度而进行更新。特别说明一下:进入这个界面有可能就不处于0% 或者一斤如就100% 或者更不不会弹出此界面!!!原因在于framework层的启动和加密的流程是异步的,而加密


的速度与当时系统 1./data 分区文件的数量 2.系统运行时 ;有很大的直接联系,这会导致加密速度波动很大。

调用加密核心算法的函数主要集中在 cryptfs_enable_all_volumes 函数中(加密算法属于算法范畴,一般不会修改,我们这边不做讨论),加密完成后,会设置ro.crypto.state = encrypted

依赖tmpfs 而起来的setting应用中的CryptKeeper.java 被触发显示一个输入PIN 的界面,这里的pin码就是之前我们说的“除了滑屏解锁的密码”:

 

后续的流程如上图

4.2 设置了pin码后加密完成后的开机流程
 

4.3没有设置密码,加密后开机 时序

 

4.3 开机后用户主动进行加密

 

加密的主要动作在Cryptfs.cpp 的 cryptfs_enable_internal函数中。

 

5 附件

jude 中画的 各种情况下的时序图:

 

猜你喜欢

转载自blog.csdn.net/pirionFordring/article/details/83420294
今日推荐