From 8ddbe40a8a8708dac7c472fa8c098c8f7b24534c Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Mon, 17 Jan 2011 15:26:29 -0800 Subject: [PATCH] Updates to cryptfs framework. Update the enable inplace API to allow the UI to show a progress bar. Add new command changepw (whichis currently not working) Internal restructuring of code to support these two features. Some minor cleanup of the code as well. Change-Id: I11461fc9ce66965bea6cd0b6bb2ff48bcf607b97 --- CommandListener.cpp | 13 +- cryptfs.c | 323 +++++++++++++++++++++++++++++++------------- cryptfs.h | 1 + 3 files changed, 239 insertions(+), 98 deletions(-) diff --git a/CommandListener.cpp b/CommandListener.cpp index c38a6d1..03cb179 100644 --- a/CommandListener.cpp +++ b/CommandListener.cpp @@ -514,8 +514,6 @@ CommandListener::CryptfsCmd::CryptfsCmd() : int CommandListener::CryptfsCmd::runCommand(SocketClient *cli, int argc, char **argv) { - dumpArgs(argc, argv, -1); - if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false); return 0; @@ -528,20 +526,31 @@ int CommandListener::CryptfsCmd::runCommand(SocketClient *cli, cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs checkpw ", false); return 0; } + dumpArgs(argc, argv, 2); rc = cryptfs_check_passwd(argv[2]); } else if (!strcmp(argv[1], "restart")) { if (argc != 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs restart", false); return 0; } + dumpArgs(argc, argv, -1); rc = cryptfs_restart(); } else if (!strcmp(argv[1], "enablecrypto")) { if ( (argc != 4) || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs enablecrypto ", false); return 0; } + dumpArgs(argc, argv, 3); rc = cryptfs_enable(argv[2], argv[3]); + } else if (!strcmp(argv[1], "changepw")) { + if (argc != 4) { + cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs changepw ", false); + return 0; + } + SLOGD("cryptfs changepw "); + rc = cryptfs_changepw(argv[2], argv[3]); } else { + dumpArgs(argc, argv, -1); cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false); } diff --git a/cryptfs.c b/cryptfs.c index 86cf17a..72ffc24 100644 --- a/cryptfs.c +++ b/cryptfs.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "cryptfs.h" @@ -41,9 +42,13 @@ #include "cutils/properties.h" #define DM_CRYPT_BUF_SIZE 4096 +#define DATA_MNT_POINT "/data" char *me = "cryptfs"; +static unsigned char saved_key_sha1[20] = { '\0' }; +static int key_sha1_saved = 0; + static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags) { memset(io, 0, dataSize); @@ -150,13 +155,7 @@ static int get_crypt_ftr_and_key(char *real_blk_name, struct crypt_mnt_ftr *cryp * encryption info footer and key, and plenty of bytes to spare for future * growth. */ -#if 1 /* The real location, use when the enable code works */ off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET; -#else - /* For testing, I'm slapping a handbuild header after my 200 megabyte - * /data partition. So my offset if 200 megabytes */ - off = 200*1024*1024; -#endif if (lseek64(fd, off, SEEK_SET) == -1) { SLOGE("Cannot seek to real block device footer\n"); @@ -343,36 +342,31 @@ errout: } -/* If we need to debug this, look at Devmapper.cpp:dumpState(), - * It does DM_LIST_DEVICES, then iterates on each device and - * calls DM_DEV_STATUS. - */ - #define HASH_COUNT 2000 #define KEY_LEN_BYTES 16 #define IV_LEN_BYTES 16 -static int create_encrypted_random_key(char *passwd, unsigned char *master_key) +static void pbkdf2(char *passwd, unsigned char *ikey) { - int fd; - unsigned char buf[KEY_LEN_BYTES]; - unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */ - unsigned char salt[32] = { 0 }; - EVP_CIPHER_CTX e_ctx; - int encrypted_len, final_len; - - /* Get some random bits for a key */ - fd = open("/dev/urandom", O_RDONLY); - read(fd, buf, sizeof(buf)); - close(fd); + unsigned char salt[32] = { 0 }; - /* Now encrypt it with the password */ /* To Do: Make a salt based on some immutable data about this device. * IMEI, or MEID, or CPU serial number, or whatever we can find */ /* Turn the password into a key and IV that can decrypt the master key */ PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, sizeof(salt), HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey); +} + +static int encrypt_master_key(char *passwd, unsigned char *decrypted_master_key, + unsigned char *encrypted_master_key) +{ + 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; + + /* Turn the password into a key and IV that can decrypt the master key */ + pbkdf2(passwd, ikey); /* Initialize the decryption engine */ if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) { @@ -380,13 +374,14 @@ static int create_encrypted_random_key(char *passwd, unsigned char *master_key) 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, master_key, &encrypted_len, - buf, KEY_LEN_BYTES)) { + 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, master_key + encrypted_len, &final_len)) { + if (! EVP_EncryptFinal(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) { SLOGE("EVP_EncryptFinal failed\n"); return -1; } @@ -403,16 +398,11 @@ static int decrypt_master_key(char *passwd, unsigned char *encrypted_master_key, unsigned char *decrypted_master_key) { unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */ - unsigned char salt[32] = { 0 }; EVP_CIPHER_CTX d_ctx; int decrypted_len, final_len; - /* To Do: Make a salt based on some immutable data about this device. - * IMEI, or MEID, or CPU serial number, or whatever we can find - */ /* Turn the password into a key and IV that can decrypt the master key */ - PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, sizeof(salt), - HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey); + pbkdf2(passwd, ikey); /* Initialize the decryption engine */ if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) { @@ -435,6 +425,24 @@ static int decrypt_master_key(char *passwd, unsigned char *encrypted_master_key, } } +static int create_encrypted_random_key(char *passwd, unsigned char *master_key) +{ + int fd; + unsigned char buf[KEY_LEN_BYTES]; + unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */ + unsigned char salt[32] = { 0 }; + EVP_CIPHER_CTX e_ctx; + int encrypted_len, final_len; + + /* Get some random bits for a key */ + fd = open("/dev/urandom", O_RDONLY); + read(fd, buf, sizeof(buf)); + close(fd); + + /* Now encrypt it with the password */ + return encrypt_master_key(passwd, buf, master_key); +} + static int get_orig_mount_parms(char *mount_point, char *fs_type, char *real_blkdev, unsigned long *mnt_flags, char *fs_options) { @@ -482,6 +490,36 @@ static int wait_and_unmount(char *mountpoint) return rc; } +#define DATA_PREP_TIMEOUT 100 +static int prep_data_fs(void) +{ + int i; + + /* Do the prep of the /data filesystem */ + property_set("vold.post_fs_data_done", "0"); + property_set("vold.decrypt", "trigger_post_fs_data"); + SLOGD("Just triggered post_fs_data\n"); + + /* Wait a max of 25 seconds, hopefully it takes much less */ + for (i=0; i cur_pct) { + char buf[8]; + + cur_pct = new_pct; + snprintf(buf, sizeof(buf), "%lld", cur_pct); + property_set("vold.encrypt_progress", buf); + } if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { SLOGE("Error reading real_blkdev %s for inplace encrypt\n", crypto_blkdev); goto errout; @@ -756,6 +791,8 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_ } } + property_set("vold.encrypt_progress", "100"); + rc = 0; errout: @@ -767,6 +804,9 @@ errout: #define CRYPTO_ENABLE_WIPE 1 #define CRYPTO_ENABLE_INPLACE 2 + +#define FRAMEWORK_BOOT_WAIT 60 + int cryptfs_enable(char *howarg, char *passwd) { int how = 0; @@ -774,8 +814,9 @@ int cryptfs_enable(char *howarg, char *passwd) char fs_type[32], fs_options[256], mount_point[32]; unsigned long mnt_flags, nr_sec; unsigned char master_key[16], decrypted_master_key[16]; - int rc, fd; + int rc=-1, fd, i; struct crypt_mnt_ftr crypt_ftr; + char tmpfs_options[80]; if (!strcmp(howarg, "wipe")) { how = CRYPTO_ENABLE_WIPE; @@ -789,7 +830,7 @@ int cryptfs_enable(char *howarg, char *passwd) get_orig_mount_parms(mount_point, fs_type, real_blkdev, &mnt_flags, fs_options); /* The init files are setup to stop the class main and late start when - * set to 4. They also unmount the fuse filesystem /mnt/sdcard on stingray. + * vold sets trigger_shutdown_framework. */ property_set("vold.decrypt", "trigger_shutdown_framework"); SLOGD("Just asked init to shut down class main\n"); @@ -799,52 +840,142 @@ int cryptfs_enable(char *howarg, char *passwd) } /* Now unmount the /data partition. */ - if (! (rc = wait_and_unmount("/data")) ) { - /* OK, we've unmounted /data, time to setup an encrypted - * mapping, and either write a new filesystem or encrypt - * in place. - */ + if (wait_and_unmount(DATA_MNT_POINT)) { + return -1; + } - fd = open(real_blkdev, O_RDONLY); - if ( (nr_sec = get_blkdev_size(fd)) == 0) { - SLOGE("Cannot get size of block device %s\n", real_blkdev); + /* Do extra work for a better UX when doing the long inplace encryption */ + if (how == CRYPTO_ENABLE_INPLACE) { + /* Now that /data is unmounted, we need to mount a tmpfs + * /data, set a property saying we're doing inplace encryption, + * and restart the framework. + */ + property_get("ro.crypto.tmpfs_options", tmpfs_options, ""); + if (mount("tmpfs", DATA_MNT_POINT, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV, + tmpfs_options) < 0) { return -1; } - close(fd); - - /* Initialize a crypt_mnt_ftr for the partition */ - cryptfs_init_crypt_mnt_ftr(&crypt_ftr); - crypt_ftr.fs_size = nr_sec - (CRYPT_FOOTER_OFFSET / 512); - strcpy((char *)crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256"); + /* Tells the framework that inplace encryption is starting */ + property_set("vold.encrypt_progress", "startup"); - /* Make an encrypted master key */ - if (create_encrypted_random_key(passwd, master_key)) { - SLOGE("Cannot create encrypted master key\n"); + /* restart the framework. */ + /* Create necessary paths on /data */ + if (prep_data_fs()) { return -1; } - /* Write the key to the end of the partition */ - put_crypt_ftr_and_key(real_blkdev, &crypt_ftr, master_key); + /* startup service classes main and late_start */ + property_set("vold.decrypt", "trigger_restart_min_framework"); + SLOGD("Just triggered restart_min_framework\n"); - decrypt_master_key(passwd, master_key, decrypted_master_key); - create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev); + /* Wait till the framework is ready */ + for (i=0; i