Wipe userdata when password is good but it won't mount
Store salted scrypt of intermediate key in crypto header When mount fails, check if matches, and if it does return error code prompting a wipe Bug: 11477689 Change-Id: I3dcf9e0c64f2a01c8ba8eaf58df82cbe717d421b
This commit is contained in:
parent
0cd6cfcf3a
commit
d0c7b17070
2 changed files with 104 additions and 30 deletions
114
cryptfs.c
114
cryptfs.c
|
@ -1188,8 +1188,9 @@ static int encrypt_master_key(const char *passwd, const unsigned char *salt,
|
|||
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 a key and IV that can decrypt the master key */
|
||||
/* 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) {
|
||||
|
@ -1240,19 +1241,40 @@ static int encrypt_master_key(const char *passwd, const unsigned char *salt,
|
|||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int decrypt_master_key_aux(char *passwd, unsigned char *salt,
|
||||
unsigned char *encrypted_master_key,
|
||||
unsigned char *decrypted_master_key,
|
||||
kdf_func kdf, void *kdf_params)
|
||||
unsigned char *encrypted_master_key,
|
||||
unsigned char *decrypted_master_key,
|
||||
kdf_func kdf, void *kdf_params,
|
||||
unsigned char** intermediate_key,
|
||||
size_t* intermediate_key_size)
|
||||
{
|
||||
unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
|
||||
EVP_CIPHER_CTX d_ctx;
|
||||
int decrypted_len, final_len;
|
||||
|
||||
/* Turn the password into a key and IV that can decrypt the master key */
|
||||
/* Turn the password into an intermediate key and IV that can decrypt the
|
||||
master key */
|
||||
if (kdf(passwd, salt, ikey, kdf_params)) {
|
||||
SLOGE("kdf failed");
|
||||
return -1;
|
||||
|
@ -1274,9 +1296,18 @@ static int decrypt_master_key_aux(char *passwd, unsigned char *salt,
|
|||
|
||||
if (decrypted_len + final_len != KEY_LEN_BYTES) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy intermediate key if needed by params */
|
||||
if (intermediate_key && intermediate_key_size) {
|
||||
*intermediate_key = (unsigned char*) malloc(KEY_LEN_BYTES);
|
||||
if (intermediate_key) {
|
||||
memcpy(*intermediate_key, ikey, KEY_LEN_BYTES);
|
||||
*intermediate_key_size = KEY_LEN_BYTES;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
|
||||
|
@ -1294,15 +1325,18 @@ static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_pa
|
|||
}
|
||||
|
||||
static int decrypt_master_key(char *passwd, unsigned char *decrypted_master_key,
|
||||
struct crypt_mnt_ftr *crypt_ftr)
|
||||
struct crypt_mnt_ftr *crypt_ftr,
|
||||
unsigned char** intermediate_key,
|
||||
size_t* intermediate_key_size)
|
||||
{
|
||||
kdf_func kdf;
|
||||
void *kdf_params;
|
||||
int ret;
|
||||
|
||||
get_kdf_func(crypt_ftr, &kdf, &kdf_params);
|
||||
ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key, decrypted_master_key, kdf,
|
||||
kdf_params);
|
||||
ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key,
|
||||
decrypted_master_key, kdf, kdf_params,
|
||||
intermediate_key, intermediate_key_size);
|
||||
if (ret != 0) {
|
||||
SLOGW("failure decrypting master key");
|
||||
}
|
||||
|
@ -1549,14 +1583,18 @@ static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
|
|||
void *kdf_params;
|
||||
int use_keymaster = 0;
|
||||
int upgrade = 0;
|
||||
unsigned char* intermediate_key = 0;
|
||||
size_t intermediate_key_size = 0;
|
||||
|
||||
SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
|
||||
orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
|
||||
|
||||
if (! (crypt_ftr->flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
|
||||
if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr)) {
|
||||
if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr,
|
||||
&intermediate_key, &intermediate_key_size)) {
|
||||
SLOGE("Failed to decrypt master key\n");
|
||||
return -1;
|
||||
rc = -1;
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1565,7 +1603,8 @@ static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
|
|||
if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
|
||||
real_blkdev, crypto_blkdev, label)) {
|
||||
SLOGE("Error creating decrypted block device\n");
|
||||
return -1;
|
||||
rc = -1;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* If init detects an encrypted filesystem, it writes a file for each such
|
||||
|
@ -1580,25 +1619,37 @@ static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
|
|||
if (fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, tmp_mount_point)) {
|
||||
SLOGE("Error temp mounting decrypted block device\n");
|
||||
delete_crypto_blk_dev(label);
|
||||
crypt_ftr->failed_decrypt_count++;
|
||||
|
||||
/* Work out if the problem is the password or the data */
|
||||
unsigned char scrypted_intermediate_key[sizeof(crypt_ftr->
|
||||
scrypted_intermediate_key)];
|
||||
int N = 1 << crypt_ftr->N_factor;
|
||||
int r = 1 << crypt_ftr->r_factor;
|
||||
int p = 1 << crypt_ftr->p_factor;
|
||||
|
||||
rc = crypto_scrypt(intermediate_key, intermediate_key_size,
|
||||
crypt_ftr->salt, sizeof(crypt_ftr->salt),
|
||||
N, r, p, scrypted_intermediate_key,
|
||||
sizeof(scrypted_intermediate_key));
|
||||
if (rc == 0 && memcmp(scrypted_intermediate_key,
|
||||
crypt_ftr->scrypted_intermediate_key,
|
||||
sizeof(scrypted_intermediate_key)) == 0) {
|
||||
SLOGE("Right password, so wipe");
|
||||
rc = -1;
|
||||
} else {
|
||||
SLOGE(rc ? "scrypt failure, so allow retry" :
|
||||
"Wrong password, so allow retry");
|
||||
rc = ++crypt_ftr->failed_decrypt_count;
|
||||
put_crypt_ftr_and_key(crypt_ftr);
|
||||
}
|
||||
} else {
|
||||
/* Success, so just umount and we'll mount it properly when we restart
|
||||
* the framework.
|
||||
/* Success!
|
||||
* umount and we'll mount it properly when we restart the framework.
|
||||
*/
|
||||
umount(tmp_mount_point);
|
||||
crypt_ftr->failed_decrypt_count = 0;
|
||||
}
|
||||
|
||||
if (orig_failed_decrypt_count != crypt_ftr->failed_decrypt_count) {
|
||||
put_crypt_ftr_and_key(crypt_ftr);
|
||||
}
|
||||
|
||||
if (crypt_ftr->failed_decrypt_count) {
|
||||
/* We failed to mount the device, so return an error */
|
||||
rc = crypt_ftr->failed_decrypt_count;
|
||||
|
||||
} else {
|
||||
/* Woot! Success! Save the name of the crypto block device
|
||||
/* Save the name of the crypto block device
|
||||
* so we can mount it when restarting the framework.
|
||||
*/
|
||||
property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
|
||||
|
@ -1636,6 +1687,11 @@ static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
|
|||
}
|
||||
}
|
||||
|
||||
errout:
|
||||
if (intermediate_key) {
|
||||
memset(intermediate_key, 0, intermediate_key_size);
|
||||
free(intermediate_key);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1768,7 +1824,7 @@ int cryptfs_verify_passwd(char *passwd)
|
|||
/* If the device has no password, then just say the password is valid */
|
||||
rc = 0;
|
||||
} else {
|
||||
decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr);
|
||||
decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
|
||||
if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
|
||||
/* They match, the password is correct */
|
||||
rc = 0;
|
||||
|
@ -2603,7 +2659,7 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
|
|||
}
|
||||
}
|
||||
|
||||
decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr);
|
||||
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");
|
||||
|
||||
|
|
20
cryptfs.h
20
cryptfs.h
|
@ -41,6 +41,7 @@
|
|||
|
||||
#define MAX_KEY_LEN 48
|
||||
#define SALT_LEN 16
|
||||
#define SCRYPT_LEN 32
|
||||
|
||||
/* definitions of flags in the structure below */
|
||||
#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
|
||||
|
@ -115,11 +116,28 @@ struct crypt_mnt_ftr {
|
|||
set, hash of first block, used
|
||||
to validate before continuing*/
|
||||
|
||||
/* key_master key, used to sign the derived key
|
||||
/* 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];
|
||||
};
|
||||
|
||||
/* Persistant data that should be available before decryption.
|
||||
|
|
Loading…
Reference in a new issue