Android3.0 上的磁盘加密 Notes on the implementation of encryption in Android 3.0

来源:互联网 发布:淘宝网品牌 编辑:程序博客网 时间:2024/05/17 01:36

Android3.0 上的磁盘加密


 

Quick summary for 3rd parties. / 摘要:

If you want to enable encryption on your device based on Android3.0 aka Honeycomb, there are only a few requirements:

如果你想在3.0 Honeycomb设备上打开加密功能,那么需要:

  1. The /data filesystem must be on a device that presents a blockdevice interface. eMMC is used in the first devices. This isbecause the encryption is done by the dm-crypt layer in the kernel,which works at the block device layer.

    /data文件系统必须基于块设备,eMMC是首选。这是因为磁盘加密功能工作于kernel的dm-crypt模块,工作在块设备层。

  2. The function get_fs_size() in system/vold/cryptfs.c assumes thefilesystem used for /data is ext4. It's just error checking code tomake sure the filesystem doesn't extend into the last 16 Kbytes ofthe partition where the crypto footer is kept. It was useful fordevelopment when sizes were changing, but should not be requiredfor release. If you are not using ext4, you can either delete itand the call to it, or fix it to understand the filesystem you areusing.

    system/vold/cryptfs.c中的get_fs_size()函数会假定/data的文件系统是ext4,异常检查确定文件系统没有使用分区上最后16Kbytes,以确保cryptofooter的存储空间。对开发人员而言是有用的,因为它的大小会变化,但是无法释放它。如果你用的不是ext4,要么删除这个函数及其调用,要么改造它以适用你的系统。

  3. Most of the code to handle the setup and teardown of the temporaryframework is in files that are not usually required to be changedon a per device basis. However, the init..rc file will require somechanges. All services must be put in one of three classes: core,main or late_state. Services in the core class are not shutdown andrestarted when the temporary framework gets the disk password.Services in the main class are restarted when the framework isrestarted. Services in late_start are not started until after thetemporary framework is restarted. Put services here that are notrequired to be running while the temporary framework gets the diskpassword.

    Also any directories that need to be created on /data that aredevice specific need to be in the Action for post-fs-data, and thatAction must end with the command "setprop vold.post_fs_data_done1". If your init..rc file does not have a post-fs-data Action, thenthe post-fs-data Action in the main init.rc file must end with thecommand "setprop vold.post_fs_data_done 1".

    大部分控制临时framework的设置和代码是在那些通常和设备无关的代码文件中。但是init.<device>.rc需要一些修改。所有的services必须属于三个类中的一个:core,main或late_state.core类中的所有服务在临时 framework 获得磁盘密码时不会停止或重启。main类中的service会在真正的framwork重启时重启,late_start中的服务在临时framework重启时不会启动,放在late_start中的服务在临时framework获得磁盘密码的过程中不会运行。

    需要在/data目录下创建的所有目录都是需要写在post-fs-data的action,并且必须用命令"setpropvold.post_fs_data_done 1"来结束。如果你的init.<device>.rc文件没有post-fs-dataaction,那么主init.rc的post-fs-data action结束时必须执行"setpropvold.post_fs_data_done 1"。

    PS:vold会在停止main class服务后将/datamount为tmpfs,然后触发post-fs-data action的操作并等待"setpropvold.post_fs_data_done1",然后继续启动临时framework并开始加密过程。如果init.rc中不执行"setpropvold.post_fs_data_done 1",那么vold会认为异常从而重启整个系统。
     

How Android encryption works / Android加密如何工作

Disk encryption on Android is based on dm-crypt, which is a kernelfeature that works at the block device layer. Therefore, it is notusable with YAFFS, which talks directly to a raw nand flash chip,but does work with emmc and similar flash devices which presentthemselves to the kernel as a block device. The current preferredfilesystem to use on these devices is ext4, though that isindependent of whether encryption is used or not.

Android的磁盘加密基于dm-crypt,它是一个kernel的一个功能动作于块设备上。因此不能基于YAFFS文件系统,它会直接访问NANDFlash芯片,但是可以工作在eMMC及其相似的Flash设备上,只要对于kernel来说是块设备。目前设备上首选使用的文件系统是ext4,但它并不关心加密与否。

While the actual encryption work is a standard linux kernelfeature, enabling it on an Android device proved somewhat tricky.The Android system tries to avoid incorporating GPL components, sousing the cryptsetup command or libdevmapper were not availableoptions. So making the appropriate ioctl(2) calls into the kernelwas the best choice. The Android volume daemon (vold) already didthis to support moving apps to the SD card, so I chose to leveragethat work for whole disk encryption. The actual encryption used forthe filesystem for first release is 128 AES with CBC andESSIV:SHA256. The master key is encrypted with 128 bit AES viacalls to the openssl library.

实际上加密功能是linuxkernel的标准功能,但使它能够工作与Android有些棘手。Android尽量避免包含GPL成分,所以不能使用cryptsetup命令或者libdevmapper。所以使用ioctl(2)通知内核是最好的选择。Android volumedaemon(vold)已经支持了apps to SD功能,所以利用它进行整个磁盘的加密。实际用于文件系统加密的首先是128 AESCBC和ESSIV:SHA256。主键通过调用openssl库使用128bit AES加密。

Once it was decided to put the smarts in vold, it became obviousthat invoking the encryption features would be done like invokingother vold commands, by adding a new module to vold (calledcryptfs) and teaching it various commands. The commands arecheckpw, restart, enablecrypto, changepw and cryptocomplete. Theywill be described in more detail below.

这个功能添加到vold,很明显,调用加密功能将像调用其他vold命令,新的加密模块在vold(称作cryptfs)包含各种命令。命令有checkpw,restart, enablecrypto, changepw 和cryptocomplete.下面会详细说明。

The other big issue was how to get the password from the user onboot. The initial plan was to implement a minimal UI that could beinvoked from init in the initial ramdisk, and then init woulddecrypt and mount /data. However, the UI engineer said that was alot of work, and suggested instead that init communicate uponstartup to tell the framework to pop up the password entry screen,get the password, and then shutdown and have the real frameworkstarted. It was decided to go this route, and this then led to ahost of other decisions described below. In particular, init set aproperty to tell the framework to go into the special passwordentry mode, and that set the stage for much communication betweenvold, init and the framework using properties. The details aredescribed below.

另外的一个重要问题是如何在boot阶段得到密码,最初设计在ramdisk实现一个能够被init调用的轻量UI,并且初始化解密功能和mount/data,无论如何,这UI对工程师是大量的工作,取而代之的是init启动临时framework弹出password输入对话框来获得密码,然后停止临时framework并重新启动真的framework,这个方案的确定引出下面的设计。详细点说,init通过设置property告诉framework进入密码输入模式,然后作为和vold沟通的平台,init和framework之间使用properties。详细的之后描述。

Finally, there were problems around killing and restarting variousservices so that /data could be unmounted and remounted. Bringingup the temporary framework to get the user password requires that atmpfs /data filesystem be mounted, otherwise the framework will notrun. But to unmount the tmpfs /data filesystem so the realdecrypted /data filesystem could be mounted meant that everyprocess that had open files on the tmpfs /data filesystem had to bekilled and restarted on the real /data filesystem. This magic wasaccomplished by requiring all services to be in 1 of 3 groups:core, main and late_start. Core services are never shut down afterstarting. main services are shutdown and then restarted after thedisk password is entered. late_start services are not started untilafter /data has been decrypted and mounted. The magic to triggerthese actions is by setting the property vold.decrypt to variousmagic strings, which is described below. Also, a new init command"class_reset" was invented to stop a service, but allow it to berestarted with a "class_start" command. If the command "class_stop"was used instead of the new command "class_reset" the flagSVC_DISABLED was added to the state of any service stopped, whichmeans it would not be started when the command class_start was usedon its class.

最后,会涉及的问题有杀掉及重启不同的服务,/data会卸载再重新加载。启动一个临时framework来获得密码,需要mount/data为tmpfs,否则framework无法启动。但是当umount tmpfs/data然后mount实际的/data加密文件系统时,所有在tmpfs上打开文件的进程会被kill并重启到实际的/data文件系统上。这个逻辑需要所有服务属于1到3组:core,main, late_start.core服务在启动后不会停止,main服务会在输入完磁盘密码后停止并重启,late_start服务直到/data挂载并解密后才会启动。这个逻辑去触发动作是通过设置propertyvold.decrypt实现,描述在后边。同时,出现一个新的init命令"class_reset"负责停止一个服务,但是允许它通过"class_start"命令重启。如果用"class_stop"替代"class_reset",会在服务停止状态设置SVC_DISABLED标志,这意味着当服务所属class使用class_start时,它不会被启动。

Booting an encrypted system. 引导加密系统

  1. When init fails to mount /data, it assumes the filesystem isencrypted, and sets several properties: ro.crypto.state ="encrypted" vold.decrypt = 1 It then mounts a /data on a tmpfsramdisk, using parameters it picks up from ro.crypto.tmpfs_options,which is set in init.rc.

    If init was able to mount /data, it sets ro.crypto.state to"unencrypted".

    In either case, init then sets 5 properties to save the initialmount options given for /data in these properties:ro.crypto.fs_type ro.crypto.fs_real_blkdev ro.crypto.fs_mnt_pointro.crypto.fs_options ro.crypto.fs_flags (saved as an ascii 8 digithex number preceded by 0x)

    当init mount/data失败,它假定文件系统已经加密,并设置几个properties:ro.crypto.state = "encrypted"vold.decrypt = 1 表明/data被mount为tmpfsramdisk,使用init.rc设置的参数ro.crypto.tmpfs_options。

    如果init能够mount/data,会设置ro.crypto.state ="unencrypted".

    无论如何,对于/data,init会设置5个properties来保存初始mount选项:ro.crypto.fs_typero.crypto.fs_real_blkdev ro.crypto.fs_mnt_pointro.crypto.fs_options ro.crypto.fs_flags(0x开头的8个ascii字符的16进制数字)

  2. The framework starts up, and sees that vold.decrypt is set to "1".This tells the framework that it is booting on a tmpfs /data disk,and it needs to get the user password. First, however, it needs tomake sure that the disk was properly encrypted. It sends thecommand "cryptfs cryptocomplete" to vold, and vold returns 0 ifencryption was completed successfully, or -1 on internal error, or-2 if encryption was not completed successfully. Vold determinesthis by looking in the crypto footer for theCRYPTO_ENCRYPTION_IN_PROGRESS flag. If it's set, the encryptionprocess was interrupted, and there is no usable data on the device.If vold returns an error, the UI should pop up a message saying theuser needs to reboot and factory reset the device, and give theuser a button to press to do so.

    framework启动时会判断 vold.decrypt,如果设置成 "1",那么framework 知道 /data 被mount为tmpfs,必须询问用户密码。 首先,需要先要确定disk是否处于加密状态,通过给vold 发送命令 "cryptfs cryptocomplete",vold 返回0 表示加密状态,返回 -1 表示内部错误,-2 表示加密没有完成。Vold 通过cryptofooter的CRYPTO_ENCRYPTION_IN_PROGRESSflag来确定。如果它被设置了,那么表示加密过程被中断了,没有可用的存储设备。当vold 返回错误时,UI应该弹出 message 通知用户必须reboot 并 factoryreset 设备,没有别的选择。

  3. Assuming the "cryptfs cryptocomplete" command returned success, theframework should pop up a UI asking for the disk password. The UIthen sends the command "cryptfs checkpw " to vold. If thepassword is correct (which is determined by successfully mountingthe decrypted at a temporary location, then unmounting it), voldsaves the name of the decrypted block device in the propertyro.crypto.fs_crypto_blkdev, and returns status 0 to the UI. If thepassword is incorrect, it returns -1 to the UI.

    假设"cryptfs cryptocomplete" 命令返回成果,那么framework应该弹出UI询问用户磁盘密码,这个UI会发送命令"cryptfs checkpw " 给vold,如果密码正确(够成功mount磁盘到临时路径,然后再unmount),vold 保存加密块设备的名字到ro.crypto.fs_crypto_blkdev然后返回0 给 UI,如果密码错误返回 -1 给UI。

  4. The UI puts up a crypto boot graphic, and then calls vold with thecommand "cryptfs restart". vold sets the property vold.decrypt to"trigger_reset_main", which causes init.rc to do "class_resetmain". This stops all services in the main class, which allows thetmpfs /data to be unmounted. vold then mounts the decrypted real/data partition, and then preps the new partition (which may neverhave been prepped if it was encrypted with the wipe option, whichis not supported on first release). It sets the propertyvold.post_fs_data_done to "0", and then sets vold.decrypt to"trigger_post_fs_dat". This causes init.rc to run the post-fs-datacommands in init.rc and init..rc. They will create any necessarydirectories, links, et al, and then set vold.post_fs_data_done to"1". Vold waits until it sees the "1" in that property. Finally,vold sets the property vold.decrypt to "trigger_restart_framework"which causes init.rc to start services in class main again, andalso start services in class late_start for the first time sinceboot.

    Now the framework boots all its services using the decrypted /datafilesystem, and the system is ready for use.

    UI显示一个加密启动画面,然后调用"cryptfsrestart",vold设置property vold.decrypt ="trigger_reset_main",关联 init.rc 的"class_reset main",会导致停止所有属于mainclass的 services,允许unmount tmpfs的/data,vold会mount被加密的真实/data分区,并做相应准备工作(如果通过wipe选项加密,data会被清除,需要重新加载数据。不支持首次启动)。然后设置vold.post_fs_data_done ="0"和 vold.decrypt ="trigger_post_fs_dat", 它导致init.rc和init.<device>.rc执行 post-fs-data命令,创建相关的目录、连接及设置vold.post_fs_data_done ="1",Vold 会等待这个标志。最后 vold 设置vold.decrypt = "trigger_restart_framework",通知 init.rc 启动main class和late_startclass的services等首次启动的服务。

    现在,framework已经使用加密的/data文件系统启动完成并准备好。

Enablingencryption on the device. / 在设备上启用加密

For first release, we only support encrypt in place, which requiresthe framework to be shutdown, /data unmounted, and then everysector of the device encrypted, after which the device reboots togo through the process described above. Here are the details:

首次发布时,我们只支持inplace加密,要求frameworkshutdown,/data unmount,然后加密每个区块,以上操作需要reboot设备之后开始。详细如下:

  1. From the UI, the user selects to encrypt the device. The UI ensuresthat there is a full charge on the battery, and the AC adapter isplugged in. It does this to make sure there is enough power tofinish the encryption process, because if the device runs out ofpower and shuts down before it has finished encrypting, file datais left in a partially encrypted state, and the device must befactory reset (and all data lost).

    Once the user presses the final button to encrypt the device, theUI calls vold with the command "cryptfs enablecryptoinplace "where passwd is the user's lock screen password.

    用户从UI选择加密设备,UI确认电池电量充足并且插入电源, 以确保有足够电量完成加密过程,因为如果设备在加密过程完成前因为没电而关机,数据会停留在一个不完整的加密状态,设备必须进行factoryreset(所有数据将丢失)。

    一旦用户按下按钮开始加密设备,UI将通过给vold发送命令 "cryptfsenablecrypto inplace ",密码使用用户锁屏密码。
  2. vold does some error checking, and returns -1 if it can't encrypt,and prints a reason in the log. If it thinks it can, it sets theproperty vold.decrypt to "trigger_shutdown_framework". This causesinit.rc to stop services in the classes late_start and main. voldthen unmounts /mnt/sdcard and then /data.

    vold会进行一些错误检查,返回 -1代表不能加密并输出包含原因的log,如果确定可以加密,它会设置vold.decrypt="trigger_shutdown_framework",将触发init.rc停止属于late_start和main的服务进程。vold会unmount/mnt/sdcard 和 /data
  3. If doing an inplace encryption, vold then mounts a tmpfs /data(using the tmpfs options from ro.crypto.tmpfs_options) and sets theproperty vold.encrypt_progress to "0". It then preps the tmpfs/data filesystem as mentioned in step 3 for booting an encryptedsystem, and then sets the property vold.decrypt to"trigger_restart_min_framework". This causes init.rc to start themain class of services. When the framework sees thatvold.encrypt_progress is set to "0", it will bring up the progressbar UI, which queries that property every 5 seconds and updates aprogress bar.

    如果进行一个inplace加密,vold会mount/data成为tmpfs(使用tmpfs选项从ro.crypto.tmpfs_options )并且设置vold.encrypt_progress="0",为了启动一个能够进行加密系统,它会准备tmpfs的/data在这步,并且设置 vold.decrypt= "trigger_restart_min_framework",这将触发init.rc启动mainclass的服务进程,当framework看到 vold.encrypt_progress设置为 "0",会启动显示进度的UI,每5s刷新一次进度。
  4. vold then sets up the crypto mapping, which creates a virtualcrypto block device that maps onto the real block device, butencrypts each sector as it is written, and decrypts each sector asit is read. vold then creates and writes out the crypto footer.

    The crypto footer contains details on the type of encryption, andan encrypted copy of the master key to decrypt the filesystem. Themaster key is a 128 bit number created by reading from/dev/urandom. It is encrypted with a hash of the user passwordcreated with the PBKDF2 function from the SSL library. The footeralso contains a random salt (also read from /dev/urandom) used toadd entropy to the hash from PBKDF2, and prevent rainbow tableattacks on the password. Also, the flagCRYPT_ENCRYPTION_IN_PROGRESS is set in the crypto footer to detectfailure to complete the encryption process. See the file cryptfs.hfor details on the crypto footer layout. The crypto footer is keptin the last 16 Kbytes of the partition, and the /data filesystemcannot extend into that part of the partition.

    之后vold设置加密mapping,它创建了一个虚拟的加密块设备映射到实际的块设备,但加密是一块块写入,解密是一块块读取,vold会创建并写入cryptofooter 

    cryptofooter包含了加密类型的详细信息和加密秘钥,用于系统解密。主密钥通过读取/dev/urandom 创建的128bit随机值,对用户密码进行哈希加密,加密算法使用SSL库的PBKDF2函数。 footer 也同样包含随机数(也从/dev/urandom 读取), 为了增加熵而使用PBKDF2的hash算法得出,防止暴力穷举破解密码。同时,CRYPT_ENCRYPTION_IN_PROGRESS标志设置到cryptofooter中防止加密失败,cryptfs.h中有详细的crypto footer布局,cryptofooter保存在分区的最后16Kbytes上,且/data文件系统不能使用到这部分。
  5. If told was to enable encryption with wipe, vold invokes thecommand "make_ext4fs" on the crypto block device, taking care tonot include the last 16 Kbytes of the partition in thefilesystem.

    If the command was to enable inplace, vold starts a loop to readeach sector of the real block device, and then write it to thecrypto block device. This takes about an hour on a 30 Gbytepartition on the Motorola Xoom. This will vary on other hardware.The loop updates the property vold.encrypt_progress every time itencrypts another 1 percent of the partition. The UI checks thisproperty every 5 seconds and updates the progress bar when itchanges.

    如果是wipe模式的加密,vold会调用命令"make_ext4fs" 在这个加密块设备,分区上不会包含那最后的16Kbytes。

    如果是inplace模式,vold开始循环读取每块数据然后写入加密块设备,这在30Gbyte分区的MotorolaXoom上要花费约1小时。这取决于硬件。 加密进度每增加1% 会更新
    vold.encrypt_progress。UI会以5s间隔检查加密进度的变化。
  6. When either encryption method has finished successfully, voldclears the flag ENCRYPTION_IN_PROGRESS in the footer, and rebootsthe system. If the reboot fails for some reason, vold sets theproperty vold.encrypt_progress to "error_reboot_failed" and the UIshould display a message asking the user to press a button toreboot. This is not expected to ever occur.

    当各种加密全部成功时,vold会清除 footer 上的 ENCRYPTION_IN_PROGRESS标志,然后reboot系统,如果因为某些原因reboot失败,vold会设置 vold.encrypt_progress="error_reboot_failed" ,UI告知用户强行reboot,这不是预期会发生的情况。
  7. If vold detects an error during the encryption process, and if nodata has been destroyed yet and the framework is up, vold sets theproperty vold.encrypt_progress to "error_not_encrypted" and the UIshould give the user the option to reboot, telling them that theencryption process never started. If the error occurs after theframework has been torn down, but before the progress bar UI is up,vold will just reboot the system. If the reboot fails, it setsvold.encrypt_progress to "error_shutting_down" and returns -1, butthere will not be anyone to catch the error. This is not expectedto happen.

    If vold detects an error during the encryption process, it setsvold.encrypt_progress to "error_partially_encrypted" and returns-1. The UI should then display a message saying the encryptionfailed, and provide a button for the user to factory reset thedevice.

    如果vold 在加密过程中遇到错误,没有数据遭到破坏并且framework在运行,vold会设置vold.encrypt_progress ="error_not_encrypted" ,UI提示用户reboot并告知加密没有进行。如果错误发生在framework停止之后,但是在UI进度条运行之前,vold会reboot系统,如果reboot失败,会设置 vold.encrypt_progress= "error_shutting_down" 并返回 -1,但不会有谁捕捉这个错误,这不是预期发生的。

    如果vold在加密过程中遇到错误,设置vold.encrypt_progress= “ error_partially_encrypted ”,并返回-1 。然后,UI显示消息说明加密失败,并为用户提供恢复出厂设置的按钮。

Changing the password / 改变密码

To change the password for the disk encryption, the UI sends thecommand "cryptfs changepw " to vold, and voldre-encrypts the disk master key with the new password.

改变磁盘加密的密码时,UI发送命令"cryptfs changepw" 给vold,vold用disk master key重新加密。

Summary of related properties / 相关properties描述

Here is a table summarizing the various properties, their possiblevalues, and what they mean:

以下表格是关联properties、键值及描述:


vold.decrypt 1

>Set by init to tell the UI to ask for the disk pw


vold.decrypt trigger_reset_main

>Set by vold to shutdown the UI asking for the disk password


vold.decrypt trigger_post_fs_data

>Set by vold to prep /data with necessary dirs, et al.


vold.decrypt trigger_restart_framework

>Set by vold to start the real framework and all services


vold.decrypt trigger_shutdown_framework

>Set by vold to shutdown the full framework to start encryption


vold.decrypt trigger_restart_min_framework

>Set by vold to start the progress bar UI for encryption.


vold.enrypt_progress

>When the framework starts up, if this property is set, enter the progress bar UI mode.


vold.encrypt_progress  0 to 100

>The progress bar UI should display the percentage value set.


vold.encrypt_progress error_partially_encrypted

>The progress bar UIshould display a message that the encryption failed, and give the user an option to factory reset the device.


vold.encrypt_progress error_reboot_failed

>The 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_encrypted

>The progress bar UI should displaya message saying an error occured, and no data was encrypted or lost, and give the user a button to reboot the system.


vold.encrypt_progress error_shutting_down

>The progress bar UI is not running, so it's unclear who will respond to this error, and it should never happen anyway.


vold.post_fs_data_done 0

>Set by vold just before setting vold.decrypt to trigger_post_fs_data.


vold.post_fs_data_done 1

>Set by init.rc orinit.<device>.rc just after finishing the task post-fs-data.


ro.crypto.fs_crypto_blkdev

>Set by the vold command checkpw for later use by the vold command restart.


ro.crypto.stateunencrypted

>Set by init to say this system is running with an unencrypted /data


ro.crypto.stateencrypted

>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_flags                           
>These 5 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_options

>Set by init.rc with the options init should use when mounting the tmpfs /data filesystem


Summary of new init actions

A list of the new Actions that are added to init.rc and/orinit..rc:

增加到init.rcinit.<device>.rc的新的action

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
onproperty:vold.decrypt=trigger_shutdown_framework



快速概要

    如果你想在你的Android3.0的设备上添加加密功能的话,那么你首先要满足一下几点:

    1./data分区必须是在支持块设备形式的接口的设备。eMMC是首选。因为整个加密的都是基于kernel的工作于快设备之上的dm-crypt层。

    2.system/vold/cryptfs.c中的get_fs_size()函数默认把data分区的文件系统认为是ext4.它只是对该分区最后的用来存放crypto footer的16Kbyte的空间做错误检查,确保data分区的文件系统没有使用到这最后的16Kbyte的空间。因为在开发阶段,该函数在开发阶段还是蛮有用的,因为这时的data分区的大小会经常变动。但到了release阶段后就并不是是必须的了。如果说你没有使用ext4的文件系统的话,你要么就把该函数删掉并把对其的调用去掉,或者你可以修改它以适用于你所使用的文件系统。

    3.对于一个设备来说,大多数用来设置及销毁临时的framework的代码段都是存在于那些不需要被改动的文件中。然而init.*.rc文件就需要做一些改动。所有的services必须属于后面的三种类别中的一种,这三种类别为:core、main和late_state。属于core类的services在临时framework拿到passworrd后是不需要停止和重启的。属于main类别的services会在临时framework重启到真正的framework时重启。而属于late_state类别的services则是在临时framework重启进入真正的framework是才启动。所以我们可以把那些在临时framework启动时不需要启动的services放到late_state类别中。

    与此同时,那些需要在data分区创建的设备相关的一些文件夹目录需要放到post-fs-data这个Action中,该Action后必须跟上"setprop vold post_fs_data_done 1"命令。如果说在你的init.*.rc中没有post-fs-data这个Action的话,那就必须在init.rc中的post-fs-data Action后面跟上"setprop vold post_fs_data_done 1"命令。

Android加密时如何工作的

    Android的磁盘加密是基于dm-cryptd的,他是工作在kernel的block device层的一个功能之一。因此这种加密方式就不适用于直接和nand flash 芯片通信的YAFFS文件系统,但却支持emmc或类似的以块设备方式在kernel呈现的flash。目前在这些块设备上首选的文件系统是ext4,即使这与是否使用加密与否并没有什么关系。

    虽然真正的加密工作是标准的linux 内核的功能,但要在一个Android 设备上使用这一功能却变得有些复杂。因为Android 系统是要尽量的避开GPL的约束。所以使用cryptsetup或libdevmapper命令就变得不可能了。于是乎通过ioctl来调用kernel的功能成为了首选。因为Android Vold已经通过这样的方法使得系统可以把app移动到SD卡,所以我们可以利用这些已经做好的工作来帮助完成整个磁盘的加密。首次release的Android 系统加密使用的是128位的AES对称加密以及ESS:SHA256。密钥master key是使用openssl库的128位的AES加密保护。

    一旦把加密的功能加到vold的话,那么调用加密的功能就会如同调用其他的vold的功能一样简单,只需要把其作为一个模块(即cryptfs)加到vold并留出一些接口命令。如checkpw、restart、enablecrypto、changepw和cryptocomplete等。这些命令我们将会在后边描述。

    另外的一个难题就是怎么在系统启动的时候从用户那儿获取到密码。一开始的想法是构造一个轻量级的UI系统通过init把其从ramdisk启动,然后通过这个轻量级的系统来获取密码以使init能解密并挂载data分区。但UI工程师这将会带来很大的工作量并同时提出另一种想法,即让init在启动的时候让framework先进入一个密码输入的界面来获取用户密码然后在在重启进入真正的framework。最后决定使用这种方法于是也就有了接下来要描述的其他的一些决定。特别的,init是通过设置系统属性来通知framework进入密码输入界面的。与此同时,vold、init和frameork之间的通信也是通过一系列的系统属性。这些都将在后边详细描述。

    最后还有一个问题就是关于杀死和重启一部分的services以使得临时data分区能被umount及解密后的data分区能被mount上。因为要想启动临时framework来获取密码就需要先挂载一个tempfs data 分区,这样framework才能启动。但这是那些打开了tempfs data分区时的文件的services就需要被杀死使得tempfs data能被umount,并且这些services需要在解密的data分区mount上后重新启动。于是乎就有了之前说的把services分为三类,即core 、 main 和late_stae。Core类的services在一开始启动后就不会被停止及重启。main类别的services在获取到用户密码后要被停止使得tmpfs data能被umount并在真正的data分区挂载上后重启。而lata_state类别的services则在真正的data分区挂载后才启动。这一系列的操作都是通过接下来下边要描述的vold.decrypt系统属性来时实现的。与此同时一个新的用来停止services的init 命令需要被引入,那即是"class_reset",使用该命令停止的services能够再次的被"class_start"重启,这与"class_stop"命令是不一样的。使用"class_stop"命令停止后的services是不能再被”class_start“重新启动的,因为"class_stop"在停止services的同时也会把标志SVC_DISABLED设置到services的状态中。而该标志即表示该service不能再通过"class_start"重新启动。

加密的系统的启动过程

    1,当init在挂载data分区失败时,它就会先假设文件系统已经被加密过了,于是会同时设置若干的系统属性:ro.crypto.state=”encrypted“ vold.decrypt=1,然后init会使用init.rc中设置的ro.crypto.tmpfs_option的值来挂载一个基于tmpfs ramdisk 的临时data分区。

    如果说init挂载data分区时成功的或,那么它将会把ro.crypto.state设置为”unencrypted“。

    不管是哪一种情况,init都会把最初挂载data分区的option保存到一下的5个系统属性中:ro.crypto.fs_type ro.crypto.fs_real_blkdev ro.crypto.fs_mnt_point ro.crypto.fs_options ro.crypto.fs_flags(保存为0x开头的Ascii的8个十六进制数)。

    2.但framework启动的时候如果检测到vold.decrypt的值为"1"时就表明此时的framework是在tmpfs 的临时data分区上启动的,并且接下来需要进入密码输入界面来获取密码。当然这是系统会首再次确认磁盘是否真的的被加密,系统通过给vold发送"cryptfs cryptocomplete"来做检查。如果vold返回0的话那么说明data分区被加密了。返回-1表示内部出错或返回-2表示加密没有成功完成。系统是通过检查crypto footer中的CRYPTO_ENCRYPTION_IN_PROGRESS标志来判断是否有加密成功完成,如果说该标志被设置了就表明加密过程被打断了,同时设备上的数据已经不可用了。如果vold返回错误的话,系统会弹出信息框提示用户需要重启设备和对设备进行出产设置,并会给用户一个确定按钮来执行此操作。

    3.在此假设命令"cryptfs cryptocomplete"成功的返回,这时系统就会进入用户密码输入界面来获取用户加密密码。系统会通过发送命令"cryptfs checkpw"给vold来检查用户输入的密码是否真确(vold如果能成功的把解密的data分区临时的挂载并卸载就表明密码正确),如果密码正确的话,vold会把解密的data分区所在的块设备明保存到ro.crypto.fs_crypto_blkdev并同时返回一个0给系统,如果说用户输入的密码是错误的话则直接返回-1给系统。

    4.但用户输入正确的密码后,系统会弹出一个重启的界面并通过"cryptfs restart"通知vold 系统重启。vold此时会设置属性vold.decrypt的值为"trigger_reset_main",这将会触发init.rc去执行"class_reset main".该操作我们上面说过会停止main类别的service使得tmpfs 的data分区能够被umount,紧接着就是把真正的data分区挂载上并对其做一些初始化的准备(当选择了wipe option时data分区就需要做这一工作,但目前并没有支持该option)。系统接下来及时设置vold.post_fs_data_done为"0"和vold.decrypt为"trigger_post_fs_dat",这将会触发init.rc去执行init.rc或init.*.rc中的post-fs-data命令。该命令会在data分区创建一些必要的文件夹,链接等文件,然后系统又把vold.post_fs_data_done谁为"1",而vold一直在等该属性被设置为1,这是Vold就会把属性vold.decrypt设置为"trigger_restart_framework"来触发init.rc去启动main类别的service和late_start类别的service。

    到此framework就真正的启动了需要使用data分区数据的service而真正的进入了系统等待用户使用。

在设备上使用Andorid 加密功能

    对于第一次的release目前只支持in place的加密,加密设备需要framework 关闭掉并umount data分区来对设备进行逐扇区的加密。加密完后系统重启进入上面描述的启动加密的Android系统的部分的流程。下面将对加密的流程作详细的介绍。

    1.当用户通过UI选择了加密设备时,系统此时就会检测设备是否已经充满了电并且插上了AC充电器在充电以保证在整个加密的过程中有足够的电量来完成加密工作。因为在整个加密的过程中一旦设备没有了电那么设备上的 数据就会处于一个部分加密的状态。这时设备就只能做factory reset的操作(此时所有的数据将会丢失)。

    当用户通过一系列的确认后。系统就会使用使用用户的password来调用vold对设备进行加密。

    2.Vold首先会作一些错误检查来确认加密是否可以进行,如果说加密不可以进行将会返回-1并同时在log中打印出原因。如果加密可以进行的话,Vold会设置系统属性vold.decrypt的值为"tirgger_shutdown_framework"。这将会触发init.rc去停止掉late_state类和main类别的service。接下来vold会把/mnt/scard和/data umount掉。

    3.如果是以 inplace的方式来加密,vold会在tmpfs上持载一个临时的data分区(使用ro.crypto.tmpfs_options的值来作挂载参数)并同时设置系统属性vold.encrypt_progress 的值为"0"。然后vold会对tmpfs 的临时data分区的文件系统作一些准备工作就如同上边的加密系统的启动的第3步所描述的一样。紧接着vold会设置vold.decrypt为"trgger_restart_min_framework",这将会触发init.rc去把上边关掉的main类别的service重新的启动。这时framework会启动并检测到vold.encrypt_progress的值是"0"就会显示一个进度条并每5秒读一次该属性的值来更新进度条的值。

    4.Vold接下来会设置crypto映射,这将会创建一个虚拟的crypto 块设备并对应的关联映射到真正的data分区所在的块设备,但整个加密或解密都是按所写入或读出的扇区来进行的,然后就是创建并写入crypto footer。

    crypto footer包含了加密的一些信息及用来解密的密钥(master key),密钥是通过读取/dev/urandom得到的一个128位的数字。并且系统会使用SSL 库的PBKDF2函数对用户的密码hash的结果来对该密钥加密保护。crypto footer也包含了一个随机种子值(从/dev/urandom得到)用来增加PBKDF2的hash值的信息熵,并阻止password会受到rainbow table的攻击破解。与些同时,crypto footer中的CRYPT_ENCRYPTION_IN_PROGRESS也会被设置为对应的值来表明加密是否成功。更多关于crypto footer的信息可以参考cryptfs.h文件。crypto footer将会被存放在/data所在的分区的最后16Kbytes,所以data分区的文件系统是不占用到这个部分的。

    5.如果加密时使用了wipe选项,vold将会使用"make_ext4fs"来对该分区格式化,这将要注意不要把最后的16Kbytes给格进data分区的文件系统。

    如果加密是inplace方式的话,vold将会开始不断的从真正的块设备读取每一扇区的值并将其写入加密的块设备。这个过程在Motorol Xoom的30Gbyte的分区上面使用了1个小时。具体的时间会因不同的硬件有所变化。整个加密的过程中每完成一个perent就会相应的更新vold.encrypt_pogress的值,系统的UI也会每5秒钟读取该属性的值来更新进度条的状态。

    6.不管是哪一种的加密方式,在加密成功后vold会把crypt footer中的ENCRYPTION_IN_PROGRESS的值清掉并重新启动系统。如果在重启系统时有错误发生,vold会设置vold.encrypt_progress的值为"error_reboot_failde"并且UI会让用户选择重启,但这并不是我们预期发生的。

    7.如果在加密的过程中发生了错误,但止时的的数据还没有被开始加密并且此时frameork已经起来(在第3部分我们有重新启动main类别的service),vold会把属性vold.encrypt_progress的值设置为"error_not_encrypted"并且让用户选择重启及告知用户加密有进行。如果说错误发生在framework被关掉后并且在进度条出现之前。vold会直接重启系统,如果此时重启失败,vold会设置vold.encrypt_progress为"error_shutting_down"并返回-1,但这个错误是不可能被捕获到的并且不是我们的预期出现的。

     如果在真正的对块设备加密的时候检测到错误的发生,vold会设置vold.encrypt_progress为"error_partially_encrypted"并返回-1,这里系统将会弹出信息框提示用户加密失败并让用户选择对设备进行factory reset。

 

密码修改

    当要对加密密码做修改时,系统会通过"cryptfs changepw"来通知vold使用新的密码对密钥(master key)重新加密保护。当然用户更改密码就是更换锁屏的pin或密码就可以。


0 0
原创粉丝点击