Merge "vold: Use the new method of rebooting by asking init to do it"
diff --git a/Android.mk b/Android.mk
index 098c3d0..ba9b0a1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -15,13 +15,15 @@
 	Devmapper.cpp \
 	ResponseCode.cpp \
 	Xwarp.cpp \
+	VoldUtil.c \
 	fstrim.c \
 	cryptfs.c
 
 common_c_includes := \
 	$(KERNEL_HEADERS) \
 	system/extras/ext4_utils \
-	external/openssl/include
+	external/openssl/include \
+	external/scrypt/lib/crypto
 
 common_shared_libraries := \
 	libsysutils \
@@ -32,6 +34,10 @@
 	liblogwrap \
 	libcrypto
 
+common_static_libraries := \
+	libfs_mgr \
+	libscrypt_static
+
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := libvold
@@ -42,7 +48,7 @@
 
 LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
 
-LOCAL_STATIC_LIBRARIES := libfs_mgr
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
 
 LOCAL_MODULE_TAGS := eng tests
 
@@ -62,7 +68,7 @@
 
 LOCAL_SHARED_LIBRARIES := $(common_shared_libraries)
 
-LOCAL_STATIC_LIBRARIES := libfs_mgr
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/CommandListener.cpp b/CommandListener.cpp
index f306527..5de920f 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -162,11 +162,16 @@
         }
         rc = vm->unmountVolume(argv[2], force, revert);
     } else if (!strcmp(argv[1], "format")) {
-        if (argc != 3) {
-            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>", false);
+        if (argc < 3 || argc > 4 ||
+            (argc == 4 && strcmp(argv[3], "wipe"))) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path> [wipe]", false);
             return 0;
         }
-        rc = vm->formatVolume(argv[2]);
+        bool wipe = false;
+        if (argc >= 4 && !strcmp(argv[3], "wipe")) {
+            wipe = true;
+        }
+        rc = vm->formatVolume(argv[2], wipe);
     } else if (!strcmp(argv[1], "share")) {
         if (argc != 4) {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
diff --git a/Fat.cpp b/Fat.cpp
index 807f440..c967a90 100644
--- a/Fat.cpp
+++ b/Fat.cpp
@@ -30,6 +30,8 @@
 #include <sys/mman.h>
 #include <sys/mount.h>
 #include <sys/wait.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
 
 #include <linux/kdev_t.h>
 
@@ -167,12 +169,16 @@
     return rc;
 }
 
-int Fat::format(const char *fsPath, unsigned int numSectors) {
+int Fat::format(const char *fsPath, unsigned int numSectors, bool wipe) {
     int fd;
     const char *args[10];
     int rc;
     int status;
 
+    if (wipe) {
+        Fat::wipe(fsPath, numSectors);
+    }
+
     args[0] = MKDOSFS_PATH;
     args[1] = "-F";
     args[2] = "32";
@@ -220,3 +226,30 @@
     }
     return 0;
 }
+
+void Fat::wipe(const char *fsPath, unsigned int numSectors) {
+    int fd;
+    unsigned long long range[2];
+
+    fd = open(fsPath, O_RDWR);
+    if (fd >= 0) {
+        if (numSectors == 0) {
+            numSectors = get_blkdev_size(fd);
+        }
+        if (numSectors == 0) {
+            SLOGE("Fat wipe failed to determine size of %s", fsPath);
+            close(fd);
+            return;
+        }
+        range[0] = 0;
+        range[1] = (unsigned long long)numSectors * 512;
+        if (ioctl(fd, BLKDISCARD, &range) < 0) {
+            SLOGE("Fat wipe failed to discard blocks on %s", fsPath);
+        } else {
+            SLOGI("Fat wipe %d sectors on %s succeeded", numSectors, fsPath);
+        }
+        close(fd);
+    } else {
+        SLOGE("Fat wipe failed to open device %s", fsPath);
+    }
+}
diff --git a/Fat.h b/Fat.h
index e02d88c..19614d1 100644
--- a/Fat.h
+++ b/Fat.h
@@ -26,7 +26,10 @@
                        bool ro, bool remount, bool executable,
                        int ownerUid, int ownerGid, int permMask,
                        bool createLost);
-    static int format(const char *fsPath, unsigned int numSectors);
+    static int format(const char *fsPath, unsigned int numSectors, bool wipe);
+
+private:
+    static void wipe(const char *fsPath, unsigned int numSectors);
 };
 
 #endif
diff --git a/VoldUtil.c b/VoldUtil.c
new file mode 100644
index 0000000..b5f9946
--- /dev/null
+++ b/VoldUtil.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+unsigned int get_blkdev_size(int fd)
+{
+  unsigned int nr_sec;
+
+  if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+    nr_sec = 0;
+  }
+
+  return nr_sec;
+}
diff --git a/VoldUtil.h b/VoldUtil.h
index 30a3add..469489a 100644
--- a/VoldUtil.h
+++ b/VoldUtil.h
@@ -17,6 +17,12 @@
 #ifndef _VOLDUTIL_H
 #define _VOLDUTIL_H
 
+#include <sys/cdefs.h>
+
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
+__BEGIN_DECLS
+  unsigned int get_blkdev_size(int fd);
+__END_DECLS
+
 #endif
diff --git a/Volume.cpp b/Volume.cpp
index 4a00ccc..4501e90 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -206,7 +206,7 @@
     return 0;
 }
 
-int Volume::formatVol() {
+int Volume::formatVol(bool wipe) {
 
     if (getState() == Volume::State_NoMedia) {
         errno = ENODEV;
@@ -250,7 +250,7 @@
         SLOGI("Formatting volume %s (%s)", getLabel(), devicePath);
     }
 
-    if (Fat::format(devicePath, 0)) {
+    if (Fat::format(devicePath, 0, wipe)) {
         SLOGE("Failed to format (%s)", strerror(errno));
         goto err;
     }
diff --git a/Volume.h b/Volume.h
index c717d4d..22e247d 100644
--- a/Volume.h
+++ b/Volume.h
@@ -67,7 +67,7 @@
 
     int mountVol();
     int unmountVol(bool force, bool revert);
-    int formatVol();
+    int formatVol(bool wipe);
 
     const char *getLabel() { return mLabel; }
     const char *getMountpoint() { return mMountpoint; }
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index a1930d1..180387c 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -167,7 +167,7 @@
     return 0;
 }
 
-int VolumeManager::formatVolume(const char *label) {
+int VolumeManager::formatVolume(const char *label, bool wipe) {
     Volume *v = lookupVolume(label);
 
     if (!v) {
@@ -180,7 +180,7 @@
         return -1;
     }
 
-    return v->formatVol();
+    return v->formatVol(wipe);
 }
 
 int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) {
@@ -414,7 +414,7 @@
         if (usingExt4) {
             formatStatus = Ext4::format(dmDevice, mountPoint);
         } else {
-            formatStatus = Fat::format(dmDevice, numImgSectors);
+            formatStatus = Fat::format(dmDevice, numImgSectors, 0);
         }
 
         if (formatStatus < 0) {
diff --git a/VolumeManager.h b/VolumeManager.h
index 198b5a9..be78516 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -83,7 +83,7 @@
     int shareVolume(const char *label, const char *method);
     int unshareVolume(const char *label, const char *method);
     int shareEnabled(const char *path, const char *method, bool *enabled);
-    int formatVolume(const char *label);
+    int formatVolume(const char *label, bool wipe);
     void disableVolumeManager(void) { mVolManagerDisabled = 1; }
 
     /* ASEC */
diff --git a/cryptfs.c b/cryptfs.c
index 2f8381c..266481d 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -45,6 +45,8 @@
 #include "cutils/android_reboot.h"
 #include "hardware_legacy/power.h"
 #include "VolumeManager.h"
+#include "VoldUtil.h"
+#include "crypto_scrypt.h"
 
 #define DM_CRYPT_BUF_SIZE 4096
 #define DATA_MNT_POINT "/data"
@@ -96,6 +98,55 @@
     }
 }
 
+/**
+ * Gets the default device scrypt parameters for key derivation time tuning.
+ * The parameters should lead to about one second derivation time for the
+ * given device.
+ */
+static void get_device_scrypt_params(struct crypt_mnt_ftr *ftr) {
+    const int default_params[] = SCRYPT_DEFAULTS;
+    int params[] = SCRYPT_DEFAULTS;
+    char paramstr[PROPERTY_VALUE_MAX];
+    char *token;
+    char *saveptr;
+    int i;
+
+    property_get(SCRYPT_PROP, paramstr, "");
+    if (paramstr[0] != '\0') {
+        /*
+         * The token we're looking for should be three integers separated by
+         * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+         */
+        for (token = strtok_r(paramstr, ":", &saveptr); token != NULL && i < 3;
+                i++, token = strtok_r(NULL, ":", &saveptr)) {
+            char *endptr;
+            params[i] = strtol(token, &endptr, 10);
+
+            /*
+             * Check that there was a valid number and it's 8-bit. If not,
+             * break out and the end check will take the default values.
+             */
+            if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+                break;
+            }
+        }
+
+        /*
+         * If there were not enough tokens or a token was malformed (not an
+         * integer), it will end up here and the default parameters can be
+         * taken.
+         */
+        if ((i != 3) || (token != NULL)) {
+            SLOGW("bad scrypt parameters '%s' should be like '12:8:1'; using defaults", paramstr);
+            memcpy(params, default_params, sizeof(params));
+        }
+    }
+
+    ftr->N_factor = params[0];
+    ftr->r_factor = params[1];
+    ftr->p_factor = params[2];
+}
+
 static unsigned int get_fs_size(char *dev)
 {
     int fd, block_size;
@@ -127,17 +178,6 @@
     return (unsigned int) (len / 512);
 }
 
-static unsigned int get_blkdev_size(int fd)
-{
-  unsigned int nr_sec;
-
-  if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
-    nr_sec = 0;
-  }
-
-  return nr_sec;
-}
-
 static int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
 {
   static int cached_data = 0;
@@ -272,46 +312,55 @@
  */
 static void upgrade_crypt_ftr(int fd, struct crypt_mnt_ftr *crypt_ftr, off64_t offset)
 {
-    struct crypt_persist_data *pdata;
-    off64_t pdata_offset = offset + CRYPT_FOOTER_TO_PERSIST_OFFSET;
+    int orig_major = crypt_ftr->major_version;
+    int orig_minor = crypt_ftr->minor_version;
 
-    /* This routine can currently only handle upgrading from 1.0 to 1.1.
-     * Do nothing if the passed structure is not version 1.0
-     */
+    if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version == 0)) {
+        struct crypt_persist_data *pdata;
+        off64_t pdata_offset = offset + CRYPT_FOOTER_TO_PERSIST_OFFSET;
 
-    if ((crypt_ftr->major_version != 1) && (crypt_ftr->minor_version != 0)) {
-        return;
+        SLOGW("upgrading crypto footer to 1.1");
+
+        pdata = malloc(CRYPT_PERSIST_DATA_SIZE);
+        if (pdata == NULL) {
+            SLOGE("Cannot allocate persisent data\n");
+            return;
+        }
+        memset(pdata, 0, CRYPT_PERSIST_DATA_SIZE);
+
+        /* Need to initialize the persistent data area */
+        if (lseek64(fd, pdata_offset, SEEK_SET) == -1) {
+            SLOGE("Cannot seek to persisent data offset\n");
+            return;
+        }
+        /* Write all zeros to the first copy, making it invalid */
+        unix_write(fd, pdata, CRYPT_PERSIST_DATA_SIZE);
+
+        /* Write a valid but empty structure to the second copy */
+        init_empty_persist_data(pdata, CRYPT_PERSIST_DATA_SIZE);
+        unix_write(fd, pdata, CRYPT_PERSIST_DATA_SIZE);
+
+        /* Update the footer */
+        crypt_ftr->persist_data_size = CRYPT_PERSIST_DATA_SIZE;
+        crypt_ftr->persist_data_offset[0] = pdata_offset;
+        crypt_ftr->persist_data_offset[1] = pdata_offset + CRYPT_PERSIST_DATA_SIZE;
+        crypt_ftr->minor_version = 1;
     }
 
-    pdata = malloc(CRYPT_PERSIST_DATA_SIZE);
-    if (pdata == NULL) {
-        SLOGE("Cannot allocate persisent data\n");
-        return;
+    if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version)) {
+        SLOGW("upgrading crypto footer to 1.2");
+        crypt_ftr->kdf_type = KDF_PBKDF2;
+        get_device_scrypt_params(crypt_ftr);
+        crypt_ftr->minor_version = 2;
     }
-    memset(pdata, 0, CRYPT_PERSIST_DATA_SIZE);
 
-    /* Need to initialize the persistent data area */
-    if (lseek64(fd, pdata_offset, SEEK_SET) == -1) {
-        SLOGE("Cannot seek to persisent data offset\n");
-        return;
+    if ((orig_major != crypt_ftr->major_version) || (orig_minor != crypt_ftr->minor_version)) {
+        if (lseek64(fd, offset, SEEK_SET) == -1) {
+            SLOGE("Cannot seek to crypt footer\n");
+            return;
+        }
+        unix_write(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr));
     }
-    /* Write all zeros to the first copy, making it invalid */
-    unix_write(fd, pdata, CRYPT_PERSIST_DATA_SIZE);
-
-    /* Write a valid but empty structure to the second copy */
-    init_empty_persist_data(pdata, CRYPT_PERSIST_DATA_SIZE);
-    unix_write(fd, pdata, CRYPT_PERSIST_DATA_SIZE);
-
-    /* Update the footer */
-    crypt_ftr->persist_data_size = CRYPT_PERSIST_DATA_SIZE;
-    crypt_ftr->persist_data_offset[0] = pdata_offset;
-    crypt_ftr->persist_data_offset[1] = pdata_offset + CRYPT_PERSIST_DATA_SIZE;
-    crypt_ftr->minor_version = 1;
-    if (lseek64(fd, offset, SEEK_SET) == -1) {
-        SLOGE("Cannot seek to crypt footer\n");
-        return;
-    }
-    unix_write(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr));
 }
 
 
@@ -361,21 +410,21 @@
     goto errout;
   }
 
-  if (crypt_ftr->major_version != 1) {
-    SLOGE("Cannot understand major version %d real block device footer\n",
-          crypt_ftr->major_version);
+  if (crypt_ftr->major_version != CURRENT_MAJOR_VERSION) {
+    SLOGE("Cannot understand major version %d real block device footer; expected %d\n",
+          crypt_ftr->major_version, CURRENT_MAJOR_VERSION);
     goto errout;
   }
 
-  if ((crypt_ftr->minor_version != 0) && (crypt_ftr->minor_version != 1)) {
-    SLOGW("Warning: crypto footer minor version %d, expected 0 or 1, continuing...\n",
-          crypt_ftr->minor_version);
+  if (crypt_ftr->minor_version > CURRENT_MINOR_VERSION) {
+    SLOGW("Warning: crypto footer minor version %d, expected <= %d, continuing...\n",
+          crypt_ftr->minor_version, CURRENT_MINOR_VERSION);
   }
 
   /* If this is a verion 1.0 crypt_ftr, make it a 1.1 crypt footer, and update the
    * copy on disk before returning.
    */
-  if (crypt_ftr->minor_version == 0) {
+  if (crypt_ftr->minor_version < CURRENT_MINOR_VERSION) {
     upgrade_crypt_ftr(fd, crypt_ftr, starting_off);
   }
 
@@ -815,24 +864,37 @@
 
 }
 
-static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey)
-{
+static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey, void *params) {
     /* Turn the password into a key and IV that can decrypt the master key */
     PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN,
                            HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
 }
 
+static void scrypt(char *passwd, unsigned char *salt, unsigned char *ikey, void *params) {
+    struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+    int N = 1 << ftr->N_factor;
+    int r = 1 << ftr->r_factor;
+    int p = 1 << ftr->p_factor;
+
+    /* Turn the password into a key and IV that can decrypt the master key */
+    crypto_scrypt((unsigned char *) passwd, strlen(passwd), salt, SALT_LEN, N, r, p, ikey,
+            KEY_LEN_BYTES + IV_LEN_BYTES);
+}
+
 static int encrypt_master_key(char *passwd, unsigned char *salt,
                               unsigned char *decrypted_master_key,
-                              unsigned char *encrypted_master_key)
+                              unsigned char *encrypted_master_key,
+                              struct crypt_mnt_ftr *crypt_ftr)
 {
     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, salt, ikey);
-  
+    get_device_scrypt_params(crypt_ftr);
+    scrypt(passwd, salt, ikey, crypt_ftr);
+
     /* Initialize the decryption engine */
     if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
         SLOGE("EVP_EncryptInit failed\n");
@@ -861,14 +923,15 @@
 
 static int decrypt_master_key(char *passwd, unsigned char *salt,
                               unsigned char *encrypted_master_key,
-                              unsigned char *decrypted_master_key)
+                              unsigned char *decrypted_master_key,
+                              kdf_func kdf, void *kdf_params)
 {
   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 */
-  pbkdf2(passwd, salt, ikey);
+  kdf(passwd, salt, ikey, kdf_params);
 
   /* Initialize the decryption engine */
   if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
@@ -891,8 +954,47 @@
   }
 }
 
-static int create_encrypted_random_key(char *passwd, unsigned char *master_key, unsigned char *salt)
+static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
 {
+    if (ftr->kdf_type == KDF_SCRYPT) {
+        *kdf = scrypt;
+        *kdf_params = ftr;
+    } else {
+        *kdf = pbkdf2;
+        *kdf_params = NULL;
+    }
+}
+
+static int decrypt_master_key_and_upgrade(char *passwd, unsigned char *decrypted_master_key,
+        struct crypt_mnt_ftr *crypt_ftr)
+{
+    kdf_func kdf;
+    void *kdf_params;
+    int ret;
+
+    get_kdf_func(crypt_ftr, &kdf, &kdf_params);
+    ret = decrypt_master_key(passwd, crypt_ftr->salt, crypt_ftr->master_key, decrypted_master_key, kdf,
+            kdf_params);
+    if (ret != 0) {
+        SLOGW("failure decrypting master key");
+        return ret;
+    }
+
+    /*
+     * Upgrade if we're not using the latest KDF.
+     */
+    if (crypt_ftr->kdf_type != KDF_SCRYPT) {
+        crypt_ftr->kdf_type = KDF_SCRYPT;
+        encrypt_master_key(passwd, crypt_ftr->salt, decrypted_master_key, crypt_ftr->master_key,
+                crypt_ftr);
+        put_crypt_ftr_and_key(crypt_ftr);
+    }
+
+    return ret;
+}
+
+static int create_encrypted_random_key(char *passwd, unsigned char *master_key, unsigned char *salt,
+        struct crypt_mnt_ftr *crypt_ftr) {
     int fd;
     unsigned char key_buf[KEY_LEN_BYTES];
     EVP_CIPHER_CTX e_ctx;
@@ -905,7 +1007,7 @@
     close(fd);
 
     /* Now encrypt it with the password */
-    return encrypt_master_key(passwd, salt, key_buf, master_key);
+    return encrypt_master_key(passwd, salt, key_buf, master_key, crypt_ftr);
 }
 
 static int wait_and_unmount(char *mountpoint)
@@ -1107,6 +1209,8 @@
   unsigned int orig_failed_decrypt_count;
   char encrypted_state[PROPERTY_VALUE_MAX];
   int rc;
+  kdf_func kdf;
+  void *kdf_params;
 
   property_get("ro.crypto.state", encrypted_state, "");
   if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
@@ -1125,7 +1229,7 @@
   orig_failed_decrypt_count = crypt_ftr.failed_decrypt_count;
 
   if (! (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
-    decrypt_master_key(passwd, crypt_ftr.salt, crypt_ftr.master_key, decrypted_master_key);
+    decrypt_master_key_and_upgrade(passwd, decrypted_master_key, &crypt_ftr);
   }
 
   if (create_crypto_blk_dev(&crypt_ftr, decrypted_master_key,
@@ -1278,8 +1382,7 @@
         /* If the device has no password, then just say the password is valid */
         rc = 0;
     } else {
-        decrypt_master_key(passwd, crypt_ftr.salt, crypt_ftr.master_key,
-                           decrypted_master_key);
+        decrypt_master_key_and_upgrade(passwd, decrypted_master_key, &crypt_ftr);
         if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
             /* They match, the password is correct */
             rc = 0;
@@ -1304,11 +1407,14 @@
 
     memset(ftr, 0, sizeof(struct crypt_mnt_ftr));
     ftr->magic = CRYPT_MNT_MAGIC;
-    ftr->major_version = 1;
-    ftr->minor_version = 1;
+    ftr->major_version = CURRENT_MAJOR_VERSION;
+    ftr->minor_version = CURRENT_MINOR_VERSION;
     ftr->ftr_size = sizeof(struct crypt_mnt_ftr);
     ftr->keysize = KEY_LEN_BYTES;
 
+    ftr->kdf_type = KDF_SCRYPT;
+    get_device_scrypt_params(ftr);
+
     ftr->persist_data_size = CRYPT_PERSIST_DATA_SIZE;
     if (get_crypt_ftr_info(NULL, &off) == 0) {
         ftr->persist_data_offset[0] = off + CRYPT_FOOTER_TO_PERSIST_OFFSET;
@@ -1614,7 +1720,7 @@
     strcpy((char *)crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
 
     /* Make an encrypted master key */
-    if (create_encrypted_random_key(passwd, crypt_ftr.master_key, crypt_ftr.salt)) {
+    if (create_encrypted_random_key(passwd, crypt_ftr.master_key, crypt_ftr.salt, &crypt_ftr)) {
         SLOGE("Cannot create encrypted master key\n");
         goto error_unencrypted;
     }
@@ -1636,7 +1742,7 @@
         save_persistent_data();
     }
 
-    decrypt_master_key(passwd, crypt_ftr.salt, crypt_ftr.master_key, decrypted_master_key);
+    decrypt_master_key_and_upgrade(passwd, decrypted_master_key, &crypt_ftr);
     create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev,
                           "userdata");
 
@@ -1784,7 +1890,7 @@
       return -1;
     }
 
-    encrypt_master_key(newpw, crypt_ftr.salt, saved_master_key, crypt_ftr.master_key);
+    encrypt_master_key(newpw, crypt_ftr.salt, saved_master_key, crypt_ftr.master_key, &crypt_ftr);
 
     /* save the key */
     put_crypt_ftr_and_key(&crypt_ftr);
diff --git a/cryptfs.h b/cryptfs.h
index 4ac8a17..421181e 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -28,6 +28,10 @@
 
 #include <cutils/properties.h>
 
+/* The current cryptfs version */
+#define CURRENT_MAJOR_VERSION 1
+#define CURRENT_MINOR_VERSION 2
+
 #define CRYPT_FOOTER_OFFSET 0x4000
 #define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
 #define CRYPT_PERSIST_DATA_SIZE 0x1000
@@ -45,8 +49,16 @@
 #define CRYPT_MNT_MAGIC 0xD0B5B1C4
 #define PERSIST_DATA_MAGIC 0xE950CD44
 
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS { 15, 3, 1 }
+
+/* Key Derivation Function algorithms */
+#define KDF_PBKDF2 1
+#define KDF_SCRYPT 2
+
 #define __le32 unsigned int
-#define __le16 unsigned short int 
+#define __le16 unsigned short int
+#define __le8  unsigned char
 
 struct crypt_mnt_ftr {
   __le32 magic;		/* See above */
@@ -71,6 +83,13 @@
 
   __le32 persist_data_size;       /* The number of bytes allocated to each copy of the
                                    * persistent data table*/
+
+  __le8  kdf_type; /* The key derivation function used. */
+
+  /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
+  __le8  N_factor; /* (1 << N) */
+  __le8  r_factor; /* (1 << r) */
+  __le8  p_factor; /* (1 << p) */
 };
 
 /* Persistant data that should be available before decryption.
@@ -114,6 +133,9 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+
+  typedef void (*kdf_func)(char *passwd, unsigned char *salt, unsigned char *ikey, void *params);
+
   int cryptfs_crypto_complete(void);
   int cryptfs_check_passwd(char *pw);
   int cryptfs_verify_passwd(char *newpw);