diff options
Diffstat (limited to '')
51 files changed, 8904 insertions, 0 deletions
diff --git a/crypto/cryptfs/Android.mk b/crypto/cryptfs/Android.mk new file mode 100644 index 000000000..f0388c228 --- /dev/null +++ b/crypto/cryptfs/Android.mk @@ -0,0 +1,54 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +ifeq ($(TW_INCLUDE_CRYPTO), true) +LOCAL_SRC_FILES:= \ + cryptfs.c + +LOCAL_CFLAGS:= -g -c -W -I../fs_mgr/include +LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO +LOCAL_CFLAGS += -DCRYPTO_FS_TYPE=\"$(TW_CRYPTO_FS_TYPE)\" +LOCAL_CFLAGS += -DCRYPTO_REAL_BLKDEV=\"$(TW_CRYPTO_REAL_BLKDEV)\" +LOCAL_CFLAGS += -DCRYPTO_MNT_POINT=\"$(TW_CRYPTO_MNT_POINT)\" +LOCAL_CFLAGS += -DCRYPTO_FS_OPTIONS=\"$(TW_CRYPTO_FS_OPTIONS)\" +LOCAL_CFLAGS += -DCRYPTO_FS_FLAGS=\"$(TW_CRYPTO_FS_FLAGS)\" +LOCAL_CFLAGS += -DCRYPTO_KEY_LOC=\"$(TW_CRYPTO_KEY_LOC)\" +ifdef TW_CRYPTO_SD_REAL_BLKDEV + LOCAL_CFLAGS += -DCRYPTO_SD_REAL_BLKDEV=\"$(TW_CRYPTO_SD_REAL_BLKDEV)\" + LOCAL_CFLAGS += -DCRYPTO_SD_FS_TYPE=\"$(TW_CRYPTO_SD_FS_TYPE)\" +endif +ifneq ($(TW_INTERNAL_STORAGE_PATH),) + LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_PATH=$(TW_INTERNAL_STORAGE_PATH) +endif +ifneq ($(TW_INTERNAL_STORAGE_MOUNT_POINT),) + LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_MOUNT_POINT=$(TW_INTERNAL_STORAGE_MOUNT_POINT) +endif +ifneq ($(TW_EXTERNAL_STORAGE_PATH),) + LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_PATH=$(TW_EXTERNAL_STORAGE_PATH) +endif +ifneq ($(TW_EXTERNAL_STORAGE_MOUNT_POINT),) + LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_MOUNT_POINT=$(TW_EXTERNAL_STORAGE_MOUNT_POINT) +endif + +LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include +LOCAL_MODULE:=cryptfs +LOCAL_MODULE_TAGS:= eng +LOCAL_SHARED_LIBRARIES += libc libcutils +LOCAL_SHARED_LIBRARIES += libcrypto + + +#LOCAL_LDFLAGS += -L$(TARGET_OUT_SHARED_LIBRARIES) -lsec_km -lsec_ecryptfs -ldl +LOCAL_LDFLAGS += -ldl + +LOCAL_STATIC_LIBRARIES += libmtdutils +LOCAL_STATIC_LIBRARIES += libminzip libunz +LOCAL_STATIC_LIBRARIES += libpixelflinger_static libpng libmincrypttwrp +LOCAL_SHARED_LIBRARIES += libz libc libstlport libcutils libstdc++ libext4_utils +LOCAL_STATIC_LIBRARIES += libcrypt_samsung + + +LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) +#LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +include $(BUILD_EXECUTABLE) +endif diff --git a/crypto/cryptfs/cryptfs.c b/crypto/cryptfs/cryptfs.c new file mode 100644 index 000000000..59e7added --- /dev/null +++ b/crypto/cryptfs/cryptfs.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013 a3955269 all rights reversed, no rights reserved. + */ + +#define TW_INCLUDE_CRYPTO_SAMSUNG +#include "../ics/cryptfs.c" + +int dm_remove_device(const char *name) +{ + int r; + r = delete_crypto_blk_dev(name); + if(!r) + printf("crypto block device '%s' deleted.\n", name); + else + printf("deleting crypto block device '%s' failed. [%d - %s]\n", name, r, strerror(errno)); + return r; +} + +int ecryptfs_test(const char *pw) +{ + char pwbuf[256]; + int r; + + strcpy(pwbuf, pw); + // 0: building options without file encryption filtering. + // 1: building options with media files filtering. + // 2: building options with all new files filtering. + r = mount_ecryptfs_drive(pwbuf, "/emmc", "/emmc", 0); + printf("mount_ecryptfs_drive: %d\n", r); + r = mount("/dev/block/mmcblk1", "/emmc", "vfat", MS_RDONLY, ""); + printf("mount: %d\n", r); + + r = umount("/emmc");///dev/block/mmcblk1"); + printf("umount: %d\n", r); + + //r = unmount_ecryptfs_drive("/emmc"); + //printf("unmount_ecryptfs_drive: %d\n", r); + + return r; +} + +int main(int argc, char* argv[]) +{ + if(argc < 2) + { + printf("no args!\n"); + return 1; + } + + property_set("ro.crypto.state", "encrypted"); + + property_set("ro.crypto.fs_type", CRYPTO_FS_TYPE); + property_set("ro.crypto.fs_real_blkdev", CRYPTO_REAL_BLKDEV); + property_set("ro.crypto.fs_mnt_point", CRYPTO_MNT_POINT); + property_set("ro.crypto.fs_options", CRYPTO_FS_OPTIONS); + property_set("ro.crypto.fs_flags", CRYPTO_FS_FLAGS); + property_set("ro.crypto.keyfile.userdata", CRYPTO_KEY_LOC); + +#ifdef CRYPTO_SD_FS_TYPE + property_set("ro.crypto.sd_fs_type", CRYPTO_SD_FS_TYPE); + property_set("ro.crypto.sd_fs_real_blkdev", CRYPTO_SD_REAL_BLKDEV); + property_set("ro.crypto.sd_fs_mnt_point", EXPAND(TW_INTERNAL_STORAGE_PATH)); +#endif + + property_set("rw.km_fips_status", "ready"); + + delete_crypto_blk_dev("userdata"); + delete_crypto_blk_dev("sdcard"); + delete_crypto_blk_dev("emmc"); + + cryptfs_check_passwd(argv[1]); + + return 0; +}; diff --git a/crypto/cryptsettings/Android.mk b/crypto/cryptsettings/Android.mk new file mode 100644 index 000000000..3a5704891 --- /dev/null +++ b/crypto/cryptsettings/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +ifeq ($(TW_INCLUDE_CRYPTO), true) +LOCAL_SRC_FILES:= \ + cryptsettings.c +LOCAL_CFLAGS:= -g -c -W +LOCAL_MODULE:=cryptsettings +LOCAL_MODULE_TAGS:= eng +LOCAL_SHARED_LIBRARIES += libc libcutils +ifeq ($(TW_INCLUDE_JB_CRYPTO), true) +LOCAL_CFLAGS += -DTW_INCLUDE_JB_CRYPTO +LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp +endif +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +include $(BUILD_EXECUTABLE) +endif diff --git a/crypto/cryptsettings/cryptsettings.c b/crypto/cryptsettings/cryptsettings.c new file mode 100644 index 000000000..4fa2b9354 --- /dev/null +++ b/crypto/cryptsettings/cryptsettings.c @@ -0,0 +1,55 @@ +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <fcntl.h> +#ifdef TW_INCLUDE_JB_CRYPTO +#include "../crypto/fs_mgr/include/fs_mgr.h" +#endif + +#include "cutils/properties.h" + +#ifndef PROPERTY_VALUE_MAX +#define PROPERTY_VALUE_MAX 255 +#endif +#ifndef FSTAB_PREFIX +#define FSTAB_PREFIX "/fstab." +#endif + +int main(void) +{ + char prop[PROPERTY_VALUE_MAX]; + char key_loc[PROPERTY_VALUE_MAX]; + char blk_dev[PROPERTY_VALUE_MAX]; + char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)]; + + printf("This tool will gather the build flags needed for decryption support for TWRP.\n"); + printf("This tool comes with no warranties whatsoever.\n"); + printf("http://teamw.in\n\n"); + property_get("ro.crypto.state", prop, "encrypted"); + if (strcmp(prop, "encrypted") != 0) + printf("Your device is not encrypted, continuing anyway.\n\nTW_INCLUDE_CRYPTO := true\n"); + property_get("ro.crypto.fs_type", prop, "ERROR"); + printf("TW_CRYPTO_FS_TYPE := \"%s\"\n", prop); + property_get("ro.crypto.fs_real_blkdev", prop, "ERROR"); + printf("TW_CRYPTO_REAL_BLKDEV := \"%s\"\n", prop); + property_get("ro.crypto.fs_mnt_point", prop, "ERROR"); + printf("TW_CRYPTO_MNT_POINT := \"%s\"\n", prop); + property_get("ro.crypto.fs_options", prop, "ERROR"); + printf("TW_CRYPTO_FS_OPTIONS := \"%s\"\n", prop); + property_get("ro.crypto.fs_flags", prop, "ERROR"); + printf("TW_CRYPTO_FS_FLAGS := \"%s\"\n", prop); + property_get("ro.crypto.keyfile.userdata", prop, "footer"); + printf("TW_CRYPTO_KEY_LOC := \"%s\"\n", prop); +#ifdef TW_INCLUDE_JB_CRYPTO + printf("\n*** NEW FOR JELLY BEAN:\n"); + strcpy(fstab_filename, FSTAB_PREFIX); + property_get("ro.hardware", fstab_filename + sizeof(FSTAB_PREFIX) - 1, ""); + fs_mgr_get_crypt_info(fstab_filename, key_loc, blk_dev, sizeof(key_loc)); + printf("fstab file location: '%s'\n\nTW_INCLUDE_JB_CRYPTO := true\n", fstab_filename); +#endif + + return 0; +} diff --git a/crypto/crypttools/Android.mk b/crypto/crypttools/Android.mk new file mode 100644 index 000000000..fc62583c4 --- /dev/null +++ b/crypto/crypttools/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +ifeq ($(TW_INCLUDE_JB_CRYPTO), true) +LOCAL_SRC_FILES:= \ + getfooter.c +LOCAL_CFLAGS:= -g -c -W +LOCAL_MODULE:=getfooter +LOCAL_MODULE_TAGS:= eng +LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp libc libcutils +LOCAL_MODULE_CLASS := UTILITY_EXECUTABLES +LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_C_INCLUDES := $(commands_recovery_local_path)/crypto/jb/ +include $(BUILD_EXECUTABLE) +endif
\ No newline at end of file diff --git a/crypto/crypttools/getfooter.c b/crypto/crypttools/getfooter.c new file mode 100644 index 000000000..aa7f88e84 --- /dev/null +++ b/crypto/crypttools/getfooter.c @@ -0,0 +1,219 @@ +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/dm-ioctl.h> +#include <sys/mount.h> +#include "../fs_mgr/include/fs_mgr.h" +#include "cryptfs.h" + +#include "cutils/properties.h" + +#ifndef PROPERTY_VALUE_MAX +#define PROPERTY_VALUE_MAX 255 +#endif +#ifndef FSTAB_PREFIX +#define FSTAB_PREFIX "/fstab." +#endif +#ifndef KEY_IN_FOOTER +#define KEY_IN_FOOTER "footer" +#endif + +struct fstab *fstab; + +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; +} + +int get_crypt_ftr_info(char **metadata_fname, off64_t *off) +{ + static int cached_data = 0; + static off64_t cached_off = 0; + static char cached_metadata_fname[PROPERTY_VALUE_MAX] = ""; + int fd; + char key_loc[PROPERTY_VALUE_MAX]; + char real_blkdev[PROPERTY_VALUE_MAX]; + unsigned int nr_sec; + int rc = -1; + + fs_mgr_get_crypt_info(fstab, key_loc, real_blkdev, sizeof(key_loc)); + + if (!strcmp(key_loc, KEY_IN_FOOTER)) { + if ( (fd = open(real_blkdev, O_RDWR)) < 0) { + printf("Cannot open real block device %s\n", real_blkdev); + return -1; + } + + if ((nr_sec = get_blkdev_size(fd))) { + /* If it's an encrypted Android partition, the last 16 Kbytes contain the + * encryption info footer and key, and plenty of bytes to spare for future + * growth. + */ + strlcpy(cached_metadata_fname, real_blkdev, sizeof(cached_metadata_fname)); + cached_off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET; + cached_data = 1; + } else { + printf("Cannot get size of block device %s\n", real_blkdev); + } + close(fd); + } else { + strlcpy(cached_metadata_fname, key_loc, sizeof(cached_metadata_fname)); + cached_off = 0; + cached_data = 1; + } + + if (cached_data) { + if (metadata_fname) { + *metadata_fname = cached_metadata_fname; + } + if (off) { + *off = cached_off; + } + rc = 0; + } + + return rc; +} + +int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr) +{ + int fd; + unsigned int nr_sec, cnt; + off64_t starting_off; + int rc = -1; + char *fname = NULL; + struct stat statbuf; + + if (get_crypt_ftr_info(&fname, &starting_off)) { + printf("Unable to get crypt_ftr_info\n"); + return -1; + } + if (fname[0] != '/') { + printf("Unexpected value for crypto key location '%s'\n", fname); + //return -1; + } + if ( (fd = open(fname, O_RDWR)) < 0) { + printf("Cannot open footer file %s for get\n", fname); + return -1; + } + + /* Make sure it's 16 Kbytes in length */ + fstat(fd, &statbuf); + if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) { + printf("footer file %s is not the expected size!\n", fname); + close(fd); + return -1; + } + + /* Seek to the start of the crypt footer */ + if (lseek64(fd, starting_off, SEEK_SET) == -1) { + printf("Cannot seek to real block device footer\n"); + close(fd); + return -1; + } + + if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) { + printf("Cannot read real block device footer\n"); + close(fd); + return -1; + } + close(fd); + return 0; +} + +int main(void) +{ + char key_loc[PROPERTY_VALUE_MAX]; + char blk_dev[PROPERTY_VALUE_MAX]; + char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)]; + struct stat st; + struct crypt_mnt_ftr crypt_ftr; + int fdout; + + printf("This tool comes with no warranties whatsoever.\n"); + printf("http://teamw.in\n\n"); + strcpy(fstab_filename, FSTAB_PREFIX); + property_get("ro.hardware", fstab_filename + sizeof(FSTAB_PREFIX) - 1, ""); + + if (stat(fstab_filename, &st) != 0) { + printf("Cannot locate fstab file '%s'\n", fstab_filename); + return -1; + } + + fstab = fs_mgr_read_fstab(fstab_filename); + if (!fstab) { + printf("failed to open %s\n", fstab_filename); + return -1; + } + + fs_mgr_get_crypt_info(fstab, key_loc, blk_dev, sizeof(blk_dev)); + + if (get_crypt_ftr_and_key(&crypt_ftr)) { + printf("Error getting crypt footer and key\n"); + return -1; + } + + if ( (fdout = open("/footerfile", O_WRONLY | O_CREAT, 0644)) < 0) { + printf("Cannot open output file /footerfile\n"); + return -1; + } + if (write(fdout, (void*) &crypt_ftr, sizeof(struct crypt_mnt_ftr)) != sizeof(struct crypt_mnt_ftr)) { + printf("Failed to write footer.\n"); + } + close(fdout); + + if (!strcmp(key_loc, KEY_IN_FOOTER)) { + unsigned int nr_sec, cnt; + off64_t off = 0; + char buffer[CRYPT_FOOTER_OFFSET]; + int fd; + + printf("\n\nDumping footer from '%s'...\n", blk_dev); + if ( (fd = open(blk_dev, O_RDONLY)) < 0) { + printf("Cannot open real block device %s\n", blk_dev); + return -1; + } + + if ((nr_sec = get_blkdev_size(fd))) { + off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET; + } else { + printf("Cannot get size of block device %s\n", blk_dev); + close(fd); + return -1; + } + printf("Size is %llu, offset is %llu\n", ((off64_t)nr_sec * 512), off); + if (lseek64(fd, off, SEEK_SET) == -1) { + printf("Cannot seek to real block device footer\n"); + close(fd); + return -1; + } + + if ( (cnt = read(fd, buffer, sizeof(buffer))) != sizeof(buffer)) { + printf("Cannot read real block device footer\n"); + close(fd); + return -1; + } + close(fd); + if ( (fdout = open("/footerdump", O_WRONLY | O_CREAT, 0644)) < 0) { + printf("Cannot open output file /footerdump\n"); + return -1; + } + if (write(fdout, buffer, sizeof(buffer)) != sizeof(buffer)) { + printf("Failed to write footer.\n"); + } + close(fdout); + } + + return 0; +} diff --git a/crypto/fs_mgr/Android.mk b/crypto/fs_mgr/Android.mk new file mode 100644 index 000000000..8dd9d4ca2 --- /dev/null +++ b/crypto/fs_mgr/Android.mk @@ -0,0 +1,38 @@ +# Copyright 2011 The Android Open Source Project +ifeq ($(TW_INCLUDE_JB_CRYPTO), true) +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include + +LOCAL_MODULE:= libfs_mgrtwrp +LOCAL_SHARED_LIBRARIES := libext4_utils +LOCAL_STATIC_LIBRARIES := liblogwraptwrp libmincrypttwrp +LOCAL_C_INCLUDES += \ + system/extras/ext4_utils \ + $(commands_recovery_local_path)/libmincrypt/includes +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include + +include $(BUILD_STATIC_LIBRARY) + + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= fs_mgr_main.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include + +LOCAL_MODULE:= fs_mgrtwrp + +LOCAL_MODULE_TAGS := optional +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin +LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) + +LOCAL_STATIC_LIBRARIES := libfs_mgrtwrp liblogwraptwrp libcutils liblog libc libmincrypttwrp libext4_utils_static + +include $(BUILD_EXECUTABLE) + +endif diff --git a/crypto/fs_mgr/fs_mgr.c b/crypto/fs_mgr/fs_mgr.c new file mode 100644 index 000000000..3aa9e6039 --- /dev/null +++ b/crypto/fs_mgr/fs_mgr.c @@ -0,0 +1,948 @@ +/* + * Copyright (C) 2012 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <libgen.h> +#include <time.h> +//#include <sys/swap.h> +/* XXX These need to be obtained from kernel headers. See b/9336527 */ +#define SWAP_FLAG_PREFER 0x8000 +#define SWAP_FLAG_PRIO_MASK 0x7fff +#define SWAP_FLAG_PRIO_SHIFT 0 +#define SWAP_FLAG_DISCARD 0x10000 + +#include <linux/loop.h> +#include <private/android_filesystem_config.h> +#include <cutils/partition_utils.h> +#include <cutils/properties.h> +#include <logwrap/logwrap.h> + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" +#include "mincrypt/sha256.h" + +#include "fs_mgr_priv.h" +#include "fs_mgr_priv_verity.h" + +#define KEY_LOC_PROP "ro.crypto.keyfile.userdata" +#define KEY_IN_FOOTER "footer" + +#define E2FSCK_BIN "/system/bin/e2fsck" +#define MKSWAP_BIN "/system/bin/mkswap" + +#define FSCK_LOG_FILE "/dev/fscklogs/log" + +#define ZRAM_CONF_DEV "/sys/block/zram0/disksize" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +struct flag_list { + const char *name; + unsigned flag; +}; + +static struct flag_list mount_flags[] = { + { "noatime", MS_NOATIME }, + { "noexec", MS_NOEXEC }, + { "nosuid", MS_NOSUID }, + { "nodev", MS_NODEV }, + { "nodiratime", MS_NODIRATIME }, + { "ro", MS_RDONLY }, + { "rw", 0 }, + { "remount", MS_REMOUNT }, + { "bind", MS_BIND }, + { "rec", MS_REC }, + { "unbindable", MS_UNBINDABLE }, + { "private", MS_PRIVATE }, + { "slave", MS_SLAVE }, + { "shared", MS_SHARED }, + { "defaults", 0 }, + { 0, 0 }, +}; + +static struct flag_list fs_mgr_flags[] = { + { "wait", MF_WAIT }, + { "check", MF_CHECK }, + { "encryptable=",MF_CRYPT }, + { "nonremovable",MF_NONREMOVABLE }, + { "voldmanaged=",MF_VOLDMANAGED}, + { "length=", MF_LENGTH }, + { "recoveryonly",MF_RECOVERYONLY }, + { "swapprio=", MF_SWAPPRIO }, + { "zramsize=", MF_ZRAMSIZE }, + { "verify", MF_VERIFY }, + { "noemulatedsd", MF_NOEMULATEDSD }, + { "defaults", 0 }, + { 0, 0 }, +}; + +struct fs_mgr_flag_values { + char *key_loc; + long long part_length; + char *label; + int partnum; + int swap_prio; + unsigned int zram_size; +}; + +/* + * gettime() - returns the time in seconds of the system's monotonic clock or + * zero on error. + */ +static time_t gettime(void) +{ + struct timespec ts; + int ret; + + ret = clock_gettime(CLOCK_MONOTONIC, &ts); + if (ret < 0) { + ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno)); + return 0; + } + + return ts.tv_sec; +} + +static int wait_for_file(const char *filename, int timeout) +{ + struct stat info; + time_t timeout_time = gettime() + timeout; + int ret = -1; + + while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0)) + usleep(10000); + + return ret; +} + +static int parse_flags(char *flags, struct flag_list *fl, + struct fs_mgr_flag_values *flag_vals, + char *fs_options, int fs_options_len) +{ + int f = 0; + int i; + char *p; + char *savep; + + /* initialize flag values. If we find a relevant flag, we'll + * update the value */ + if (flag_vals) { + memset(flag_vals, 0, sizeof(*flag_vals)); + flag_vals->partnum = -1; + flag_vals->swap_prio = -1; /* negative means it wasn't specified. */ + } + + /* initialize fs_options to the null string */ + if (fs_options && (fs_options_len > 0)) { + fs_options[0] = '\0'; + } + + p = strtok_r(flags, ",", &savep); + while (p) { + /* Look for the flag "p" in the flag list "fl" + * If not found, the loop exits with fl[i].name being null. + */ + for (i = 0; fl[i].name; i++) { + if (!strncmp(p, fl[i].name, strlen(fl[i].name))) { + f |= fl[i].flag; + if ((fl[i].flag == MF_CRYPT) && flag_vals) { + /* The encryptable flag is followed by an = and the + * location of the keys. Get it and return it. + */ + flag_vals->key_loc = strdup(strchr(p, '=') + 1); + } else if ((fl[i].flag == MF_LENGTH) && flag_vals) { + /* The length flag is followed by an = and the + * size of the partition. Get it and return it. + */ + flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0); + } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) { + /* The voldmanaged flag is followed by an = and the + * label, a colon and the partition number or the + * word "auto", e.g. + * voldmanaged=sdcard:3 + * Get and return them. + */ + char *label_start; + char *label_end; + char *part_start; + + label_start = strchr(p, '=') + 1; + label_end = strchr(p, ':'); + if (label_end) { + flag_vals->label = strndup(label_start, + (int) (label_end - label_start)); + part_start = strchr(p, ':') + 1; + if (!strcmp(part_start, "auto")) { + flag_vals->partnum = -1; + } else { + flag_vals->partnum = strtol(part_start, NULL, 0); + } + } else { + ERROR("Warning: voldmanaged= flag malformed\n"); + } + } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) { + flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0); + } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) { + flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0); + } + break; + } + } + + if (!fl[i].name) { + if (fs_options) { + /* It's not a known flag, so it must be a filesystem specific + * option. Add it to fs_options if it was passed in. + */ + strlcat(fs_options, p, fs_options_len); + strlcat(fs_options, ",", fs_options_len); + } else { + /* fs_options was not passed in, so if the flag is unknown + * it's an error. + */ + ERROR("Warning: unknown flag %s\n", p); + } + } + p = strtok_r(NULL, ",", &savep); + } + +out: + if (fs_options && fs_options[0]) { + /* remove the last trailing comma from the list of options */ + fs_options[strlen(fs_options) - 1] = '\0'; + } + + return f; +} + +/* Read a line of text till the next newline character. + * If no newline is found before the buffer is full, continue reading till a new line is seen, + * then return an empty buffer. This effectively ignores lines that are too long. + * On EOF, return null. + */ +static char *fs_getline(char *buf, int size, FILE *file) +{ + int cnt = 0; + int eof = 0; + int eol = 0; + int c; + + if (size < 1) { + return NULL; + } + + while (cnt < (size - 1)) { + c = getc(file); + if (c == EOF) { + eof = 1; + break; + } + + *(buf + cnt) = c; + cnt++; + + if (c == '\n') { + eol = 1; + break; + } + } + + /* Null terminate what we've read */ + *(buf + cnt) = '\0'; + + if (eof) { + if (cnt) { + return buf; + } else { + return NULL; + } + } else if (eol) { + return buf; + } else { + /* The line is too long. Read till a newline or EOF. + * If EOF, return null, if newline, return an empty buffer. + */ + while(1) { + c = getc(file); + if (c == EOF) { + return NULL; + } else if (c == '\n') { + *buf = '\0'; + return buf; + } + } + } +} + +struct fstab *fs_mgr_read_fstab(const char *fstab_path) +{ + FILE *fstab_file; + int cnt, entries; + int len; + char line[256]; + const char *delim = " \t"; + char *save_ptr, *p; + struct fstab *fstab; + struct fstab_rec *recs; + struct fs_mgr_flag_values flag_vals; +#define FS_OPTIONS_LEN 1024 + char tmp_fs_options[FS_OPTIONS_LEN]; + + fstab_file = fopen(fstab_path, "r"); + if (!fstab_file) { + ERROR("Cannot open file %s\n", fstab_path); + return 0; + } + + entries = 0; + while (fs_getline(line, sizeof(line), fstab_file)) { + /* if the last character is a newline, shorten the string by 1 byte */ + len = strlen(line); + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + } + /* Skip any leading whitespace */ + p = line; + while (isspace(*p)) { + p++; + } + /* ignore comments or empty lines */ + if (*p == '#' || *p == '\0') + continue; + entries++; + } + + if (!entries) { + ERROR("No entries found in fstab\n"); + return 0; + } + + /* Allocate and init the fstab structure */ + fstab = calloc(1, sizeof(struct fstab)); + fstab->num_entries = entries; + fstab->fstab_filename = strdup(fstab_path); + fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec)); + + fseek(fstab_file, 0, SEEK_SET); + + cnt = 0; + while (fs_getline(line, sizeof(line), fstab_file)) { + /* if the last character is a newline, shorten the string by 1 byte */ + len = strlen(line); + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + } + + /* Skip any leading whitespace */ + p = line; + while (isspace(*p)) { + p++; + } + /* ignore comments or empty lines */ + if (*p == '#' || *p == '\0') + continue; + + /* If a non-comment entry is greater than the size we allocated, give an + * error and quit. This can happen in the unlikely case the file changes + * between the two reads. + */ + if (cnt >= entries) { + ERROR("Tried to process more entries than counted\n"); + break; + } + + if (!(p = strtok_r(line, delim, &save_ptr))) { + ERROR("Error parsing mount source\n"); + return 0; + } + fstab->recs[cnt].blk_device = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing mount_point\n"); + return 0; + } + fstab->recs[cnt].mount_point = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing fs_type\n"); + return 0; + } + fstab->recs[cnt].fs_type = strdup(p); + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing mount_flags\n"); + return 0; + } + tmp_fs_options[0] = '\0'; + fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL, + tmp_fs_options, FS_OPTIONS_LEN); + + /* fs_options are optional */ + if (tmp_fs_options[0]) { + fstab->recs[cnt].fs_options = strdup(tmp_fs_options); + } else { + fstab->recs[cnt].fs_options = NULL; + } + + if (!(p = strtok_r(NULL, delim, &save_ptr))) { + ERROR("Error parsing fs_mgr_options\n"); + return 0; + } + fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, + &flag_vals, NULL, 0); + fstab->recs[cnt].key_loc = flag_vals.key_loc; + fstab->recs[cnt].length = flag_vals.part_length; + fstab->recs[cnt].label = flag_vals.label; + fstab->recs[cnt].partnum = flag_vals.partnum; + fstab->recs[cnt].swap_prio = flag_vals.swap_prio; + fstab->recs[cnt].zram_size = flag_vals.zram_size; + cnt++; + } + fclose(fstab_file); + + return fstab; +} + +void fs_mgr_free_fstab(struct fstab *fstab) +{ + int i; + + if (!fstab) { + return; + } + + for (i = 0; i < fstab->num_entries; i++) { + /* Free the pointers return by strdup(3) */ + free(fstab->recs[i].blk_device); + free(fstab->recs[i].mount_point); + free(fstab->recs[i].fs_type); + free(fstab->recs[i].fs_options); + free(fstab->recs[i].key_loc); + free(fstab->recs[i].label); + i++; + } + + /* Free the fstab_recs array created by calloc(3) */ + free(fstab->recs); + + /* Free the fstab filename */ + free(fstab->fstab_filename); + + /* Free fstab */ + free(fstab); +} + +static void check_fs(char *blk_device, char *fs_type, char *target) +{ + int status; + int ret; + long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID; + char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro"; + char *e2fsck_argv[] = { + E2FSCK_BIN, + "-y", + blk_device + }; + + /* Check for the types of filesystems we know how to check */ + if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) { + /* + * First try to mount and unmount the filesystem. We do this because + * the kernel is more efficient than e2fsck in running the journal and + * processing orphaned inodes, and on at least one device with a + * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes + * to do what the kernel does in about a second. + * + * After mounting and unmounting the filesystem, run e2fsck, and if an + * error is recorded in the filesystem superblock, e2fsck will do a full + * check. Otherwise, it does nothing. If the kernel cannot mount the + * filesytsem due to an error, e2fsck is still run to do a full check + * fix the filesystem. + */ + ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts); + if (!ret) { + umount(target); + } + + INFO("Running %s on %s\n", E2FSCK_BIN, blk_device); + + ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, + &status, true, LOG_KLOG | LOG_FILE, + true, FSCK_LOG_FILE); + + if (ret < 0) { + /* No need to check for error in fork, we can't really handle it now */ + ERROR("Failed trying to run %s\n", E2FSCK_BIN); + } + } + + return; +} + +static void remove_trailing_slashes(char *n) +{ + int len; + + len = strlen(n) - 1; + while ((*(n + len) == '/') && len) { + *(n + len) = '\0'; + len--; + } +} + +/* + * Mark the given block device as read-only, using the BLKROSET ioctl. + * Return 0 on success, and -1 on error. + */ +static void fs_set_blk_ro(const char *blockdev) +{ + int fd; + int ON = 1; + + fd = open(blockdev, O_RDONLY); + if (fd < 0) { + // should never happen + return; + } + + ioctl(fd, BLKROSET, &ON); + close(fd); +} + +/* + * __mount(): wrapper around the mount() system call which also + * sets the underlying block device to read-only if the mount is read-only. + * See "man 2 mount" for return values. + */ +static int __mount(const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, + const void *data) +{ + int ret = mount(source, target, filesystemtype, mountflags, data); + + if ((ret == 0) && (mountflags & MS_RDONLY) != 0) { + fs_set_blk_ro(source); + } + + return ret; +} + +static int fs_match(char *in1, char *in2) +{ + char *n1; + char *n2; + int ret; + + n1 = strdup(in1); + n2 = strdup(in2); + + remove_trailing_slashes(n1); + remove_trailing_slashes(n2); + + ret = !strcmp(n1, n2); + + free(n1); + free(n2); + + return ret; +} + +int fs_mgr_mount_all(struct fstab *fstab) +{ + int i = 0; + int encrypted = 0; + int ret = -1; + int mret; + + if (!fstab) { + return ret; + } + + for (i = 0; i < fstab->num_entries; i++) { + /* Don't mount entries that are managed by vold */ + if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) { + continue; + } + + /* Skip swap and raw partition entries such as boot, recovery, etc */ + if (!strcmp(fstab->recs[i].fs_type, "swap") || + !strcmp(fstab->recs[i].fs_type, "emmc") || + !strcmp(fstab->recs[i].fs_type, "mtd")) { + continue; + } + + if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { + wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); + } + + if (fstab->recs[i].fs_mgr_flags & MF_CHECK) { + check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type, + fstab->recs[i].mount_point); + } + + if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) { + if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) { + ERROR("Could not set up verified partition, skipping!"); + continue; + } + } + + mret = __mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, + fstab->recs[i].fs_type, fstab->recs[i].flags, + fstab->recs[i].fs_options); + + if (!mret) { + /* Success! Go get the next one */ + continue; + } + + /* mount(2) returned an error, check if it's encrypted and deal with it */ + if ((fstab->recs[i].fs_mgr_flags & MF_CRYPT) && + !partition_wiped(fstab->recs[i].blk_device)) { + /* Need to mount a tmpfs at this mountpoint for now, and set + * properties that vold will query later for decrypting + */ + if (mount("tmpfs", fstab->recs[i].mount_point, "tmpfs", + MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS) < 0) { + ERROR("Cannot mount tmpfs filesystem for encrypted fs at %s\n", + fstab->recs[i].mount_point); + goto out; + } + encrypted = 1; + } else { + ERROR("Cannot mount filesystem on %s at %s\n", + fstab->recs[i].blk_device, fstab->recs[i].mount_point); + goto out; + } + } + + if (encrypted) { + ret = 1; + } else { + ret = 0; + } + +out: + return ret; +} + +/* If tmp_mount_point is non-null, mount the filesystem there. This is for the + * tmp mount we do to check the user password + */ +int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, + char *tmp_mount_point) +{ + int i = 0; + int ret = -1; + char *m; + + if (!fstab) { + return ret; + } + + for (i = 0; i < fstab->num_entries; i++) { + if (!fs_match(fstab->recs[i].mount_point, n_name)) { + continue; + } + + /* We found our match */ + /* If this swap or a raw partition, report an error */ + if (!strcmp(fstab->recs[i].fs_type, "swap") || + !strcmp(fstab->recs[i].fs_type, "emmc") || + !strcmp(fstab->recs[i].fs_type, "mtd")) { + ERROR("Cannot mount filesystem of type %s on %s\n", + fstab->recs[i].fs_type, n_blk_device); + goto out; + } + + /* First check the filesystem if requested */ + if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { + wait_for_file(n_blk_device, WAIT_TIMEOUT); + } + + if (fstab->recs[i].fs_mgr_flags & MF_CHECK) { + check_fs(n_blk_device, fstab->recs[i].fs_type, + fstab->recs[i].mount_point); + } + + if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) { + if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) { + ERROR("Could not set up verified partition, skipping!"); + continue; + } + } + + /* Now mount it where requested */ + if (tmp_mount_point) { + m = tmp_mount_point; + } else { + m = fstab->recs[i].mount_point; + } + if (__mount(n_blk_device, m, fstab->recs[i].fs_type, + fstab->recs[i].flags, fstab->recs[i].fs_options)) { + ERROR("Cannot mount filesystem on %s at %s\n", + n_blk_device, m); + goto out; + } else { + ret = 0; + goto out; + } + } + + /* We didn't find a match, say so and return an error */ + ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point); + +out: + return ret; +} + +/* + * mount a tmpfs filesystem at the given point. + * return 0 on success, non-zero on failure. + */ +int fs_mgr_do_tmpfs_mount(char *n_name) +{ + int ret; + + ret = mount("tmpfs", n_name, "tmpfs", + MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS); + if (ret < 0) { + ERROR("Cannot mount tmpfs filesystem at %s\n", n_name); + return -1; + } + + /* Success */ + return 0; +} + +int fs_mgr_unmount_all(struct fstab *fstab) +{ + int i = 0; + int ret = 0; + + if (!fstab) { + return -1; + } + + while (fstab->recs[i].blk_device) { + if (umount(fstab->recs[i].mount_point)) { + ERROR("Cannot unmount filesystem at %s\n", fstab->recs[i].mount_point); + ret = -1; + } + i++; + } + + return ret; +} + +/* This must be called after mount_all, because the mkswap command needs to be + * available. + */ +int fs_mgr_swapon_all(struct fstab *fstab) +{ + int i = 0; + int flags = 0; + int err = 0; + int ret = 0; + int status; + char *mkswap_argv[2] = { + MKSWAP_BIN, + NULL + }; + + if (!fstab) { + return -1; + } + + for (i = 0; i < fstab->num_entries; i++) { + /* Skip non-swap entries */ + if (strcmp(fstab->recs[i].fs_type, "swap")) { + continue; + } + + if (fstab->recs[i].zram_size > 0) { + /* A zram_size was specified, so we need to configure the + * device. There is no point in having multiple zram devices + * on a system (all the memory comes from the same pool) so + * we can assume the device number is 0. + */ + FILE *zram_fp; + + zram_fp = fopen(ZRAM_CONF_DEV, "r+"); + if (zram_fp == NULL) { + ERROR("Unable to open zram conf device " ZRAM_CONF_DEV); + ret = -1; + continue; + } + fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size); + fclose(zram_fp); + } + + if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { + wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); + } + + /* Initialize the swap area */ + mkswap_argv[1] = fstab->recs[i].blk_device; + err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv, + &status, true, LOG_KLOG, false, NULL); + if (err) { + ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device); + ret = -1; + continue; + } + + /* If -1, then no priority was specified in fstab, so don't set + * SWAP_FLAG_PREFER or encode the priority */ + if (fstab->recs[i].swap_prio >= 0) { + flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) & + SWAP_FLAG_PRIO_MASK; + flags |= SWAP_FLAG_PREFER; + } else { + flags = 0; + } + // requires sys/swap.h which is not available in older trees + // this entire function does not appear to be used for decrypt + err = -1; //swapon(fstab->recs[i].blk_device, flags); + if (err) { + ERROR("swapon failed for %s\n", fstab->recs[i].blk_device); + ret = -1; + } + } + + return ret; +} + +/* + * key_loc must be at least PROPERTY_VALUE_MAX bytes long + * + * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long + */ +int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size) +{ + int i = 0; + + if (!fstab) { + return -1; + } + /* Initialize return values to null strings */ + if (key_loc) { + *key_loc = '\0'; + } + if (real_blk_device) { + *real_blk_device = '\0'; + } + + /* Look for the encryptable partition to find the data */ + for (i = 0; i < fstab->num_entries; i++) { + /* Don't deal with vold managed enryptable partitions here */ + if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) { + continue; + } + if (!(fstab->recs[i].fs_mgr_flags & MF_CRYPT)) { + continue; + } + + /* We found a match */ + if (key_loc) { + strlcpy(key_loc, fstab->recs[i].key_loc, size); + } + if (real_blk_device) { + strlcpy(real_blk_device, fstab->recs[i].blk_device, size); + } + break; + } + + return 0; +} + +/* Add an entry to the fstab, and return 0 on success or -1 on error */ +int fs_mgr_add_entry(struct fstab *fstab, + const char *mount_point, const char *fs_type, + const char *blk_device, long long length) +{ + struct fstab_rec *new_fstab_recs; + int n = fstab->num_entries; + + new_fstab_recs = (struct fstab_rec *) + realloc(fstab->recs, sizeof(struct fstab_rec) * (n + 1)); + + if (!new_fstab_recs) { + return -1; + } + + /* A new entry was added, so initialize it */ + memset(&new_fstab_recs[n], 0, sizeof(struct fstab_rec)); + new_fstab_recs[n].mount_point = strdup(mount_point); + new_fstab_recs[n].fs_type = strdup(fs_type); + new_fstab_recs[n].blk_device = strdup(blk_device); + new_fstab_recs[n].length = 0; + + /* Update the fstab struct */ + fstab->recs = new_fstab_recs; + fstab->num_entries++; + + return 0; +} + +struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path) +{ + int i; + + if (!fstab) { + return NULL; + } + + for (i = 0; i < fstab->num_entries; i++) { + int len = strlen(fstab->recs[i].mount_point); + if (strncmp(path, fstab->recs[i].mount_point, len) == 0 && + (path[len] == '\0' || path[len] == '/')) { + return &fstab->recs[i]; + } + } + + return NULL; +} + +int fs_mgr_is_voldmanaged(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_VOLDMANAGED; +} + +int fs_mgr_is_nonremovable(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_NONREMOVABLE; +} + +int fs_mgr_is_encryptable(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_CRYPT; +} + +int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_NOEMULATEDSD; +} diff --git a/crypto/fs_mgr/fs_mgr_main.c b/crypto/fs_mgr/fs_mgr_main.c new file mode 100644 index 000000000..4bde4a1da --- /dev/null +++ b/crypto/fs_mgr/fs_mgr_main.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2012 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <libgen.h> +#include "fs_mgr_priv.h" + +char *me = ""; + +static void usage(void) +{ + ERROR("%s: usage: %s <-a | -n mnt_point blk_dev | -u> <fstab_file>\n", me, me); + exit(1); +} + +/* Parse the command line. If an error is encountered, print an error message + * and exit the program, do not return to the caller. + * Return the number of argv[] entries consumed. + */ +static void parse_options(int argc, char *argv[], int *a_flag, int *u_flag, int *n_flag, + char **n_name, char **n_blk_dev) +{ + me = basename(strdup(argv[0])); + + if (argc <= 1) { + usage(); + } + + if (!strcmp(argv[1], "-a")) { + if (argc != 3) { + usage(); + } + *a_flag = 1; + } + if (!strcmp(argv[1], "-n")) { + if (argc != 5) { + usage(); + } + *n_flag = 1; + *n_name = argv[2]; + *n_blk_dev = argv[3]; + } + if (!strcmp(argv[1], "-u")) { + if (argc != 3) { + usage(); + } + *u_flag = 1; + } + + /* If no flag is specified, it's an error */ + if (!(*a_flag | *n_flag | *u_flag)) { + usage(); + } + + /* If more than one flag is specified, it's an error */ + if ((*a_flag + *n_flag + *u_flag) > 1) { + usage(); + } + + return; +} + +int main(int argc, char *argv[]) +{ + int a_flag=0; + int u_flag=0; + int n_flag=0; + char *n_name; + char *n_blk_dev; + char *fstab_file; + struct fstab *fstab; + + klog_init(); + klog_set_level(6); + + parse_options(argc, argv, &a_flag, &u_flag, &n_flag, &n_name, &n_blk_dev); + + /* The name of the fstab file is last, after the option */ + fstab_file = argv[argc - 1]; + + fstab = fs_mgr_read_fstab(fstab_file); + + if (a_flag) { + return fs_mgr_mount_all(fstab); + } else if (n_flag) { + return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0); + } else if (u_flag) { + return fs_mgr_unmount_all(fstab); + } else { + ERROR("%s: Internal error, unknown option\n", me); + exit(1); + } + + fs_mgr_free_fstab(fstab); + + /* Should not get here */ + exit(1); +} + diff --git a/crypto/fs_mgr/fs_mgr_priv.h b/crypto/fs_mgr/fs_mgr_priv.h new file mode 100644 index 000000000..59ffd785c --- /dev/null +++ b/crypto/fs_mgr/fs_mgr_priv.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef __CORE_FS_MGR_PRIV_H +#define __CORE_FS_MGR_PRIV_H + +#include <cutils/klog.h> +#include <fs_mgr.h> + +#define INFO(x...) KLOG_INFO("fs_mgr", x) +#define ERROR(x...) KLOG_ERROR("fs_mgr", x) + +#define CRYPTO_TMPFS_OPTIONS "size=128m,mode=0771,uid=1000,gid=1000" + +#define WAIT_TIMEOUT 20 + +/* fstab has the following format: + * + * Any line starting with a # is a comment and ignored + * + * Any blank line is ignored + * + * All other lines must be in this format: + * <source> <mount_point> <fs_type> <mount_flags> <fs_options> <fs_mgr_options> + * + * <mount_flags> is a comma separated list of flags that can be passed to the + * mount command. The list includes noatime, nosuid, nodev, nodiratime, + * ro, rw, remount, defaults. + * + * <fs_options> is a comma separated list of options accepted by the filesystem being + * mounted. It is passed directly to mount without being parsed + * + * <fs_mgr_options> is a comma separated list of flags that control the operation of + * the fs_mgr program. The list includes "wait", which will wait till + * the <source> file exists, and "check", which requests that the fs_mgr + * run an fscheck program on the <source> before mounting the filesystem. + * If check is specifed on a read-only filesystem, it is ignored. + * Also, "encryptable" means that filesystem can be encrypted. + * The "encryptable" flag _MUST_ be followed by a = and a string which + * is the location of the encryption keys. It can either be a path + * to a file or partition which contains the keys, or the word "footer" + * which means the keys are in the last 16 Kbytes of the partition + * containing the filesystem. + * + * When the fs_mgr is requested to mount all filesystems, it will first mount all the + * filesystems that do _NOT_ specify check (including filesystems that are read-only and + * specify check, because check is ignored in that case) and then it will check and mount + * filesystem marked with check. + * + */ + +#define MF_WAIT 0x1 +#define MF_CHECK 0x2 +#define MF_CRYPT 0x4 +#define MF_NONREMOVABLE 0x8 +#define MF_VOLDMANAGED 0x10 +#define MF_LENGTH 0x20 +#define MF_RECOVERYONLY 0x40 +#define MF_SWAPPRIO 0x80 +#define MF_ZRAMSIZE 0x100 +#define MF_VERIFY 0x200 +/* + * There is no emulated sdcard daemon running on /data/media on this device, + * so treat the physical SD card as the only external storage device, + * a la the Nexus One. + */ +#define MF_NOEMULATEDSD 0x400 + +#define DM_BUF_SIZE 4096 + +#endif /* __CORE_FS_MGR_PRIV_H */ + diff --git a/crypto/fs_mgr/fs_mgr_priv_verity.h b/crypto/fs_mgr/fs_mgr_priv_verity.h new file mode 100644 index 000000000..61937849a --- /dev/null +++ b/crypto/fs_mgr/fs_mgr_priv_verity.h @@ -0,0 +1,17 @@ +/* + * 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. + */ + +int fs_mgr_setup_verity(struct fstab_rec *fstab);
\ No newline at end of file diff --git a/crypto/fs_mgr/fs_mgr_verity.c b/crypto/fs_mgr/fs_mgr_verity.c new file mode 100644 index 000000000..969eab2a0 --- /dev/null +++ b/crypto/fs_mgr/fs_mgr_verity.c @@ -0,0 +1,410 @@ +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <libgen.h> +#include <time.h> + +#include <private/android_filesystem_config.h> +#include <logwrap/logwrap.h> + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" +#include "mincrypt/sha256.h" + +#include "ext4_utils.h" +#include "ext4.h" + +#include "fs_mgr_priv.h" +#include "fs_mgr_priv_verity.h" + +#define VERITY_METADATA_SIZE 32768 +#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001 +#define VERITY_TABLE_RSA_KEY "/verity_key" + +extern struct fs_info info; + +static RSAPublicKey *load_key(char *path) +{ + FILE *f; + RSAPublicKey *key; + + key = malloc(sizeof(RSAPublicKey)); + if (!key) { + ERROR("Can't malloc key\n"); + return NULL; + } + + f = fopen(path, "r"); + if (!f) { + ERROR("Can't open '%s'\n", path); + free(key); + return NULL; + } + + if (!fread(key, sizeof(*key), 1, f)) { + ERROR("Could not read key!"); + fclose(f); + free(key); + return NULL; + } + + if (key->len != RSANUMWORDS) { + ERROR("Invalid key length %d\n", key->len); + fclose(f); + free(key); + return NULL; + } + + fclose(f); + return key; +} + +static int verify_table(char *signature, char *table, int table_length) +{ + int fd; + RSAPublicKey *key; + uint8_t hash_buf[SHA_DIGEST_SIZE]; + int retval = -1; + + // Hash the table + SHA_hash((uint8_t*)table, table_length, hash_buf); + + // Now get the public key from the keyfile + key = load_key(VERITY_TABLE_RSA_KEY); + if (!key) { + ERROR("Couldn't load verity keys"); + goto out; + } + + // verify the result + if (!RSA_verify(key, + (uint8_t*) signature, + RSANUMBYTES, + (uint8_t*) hash_buf, + SHA_DIGEST_SIZE)) { + ERROR("Couldn't verify table."); + goto out; + } + + retval = 0; + +out: + free(key); + return retval; +} + +static int get_target_device_size(char *blk_device, uint64_t *device_size) +{ + int data_device; + struct ext4_super_block sb; + + data_device = open(blk_device, O_RDONLY); + if (data_device < 0) { + ERROR("Error opening block device (%s)", strerror(errno)); + return -1; + } + + if (lseek64(data_device, 1024, SEEK_SET) < 0) { + ERROR("Error seeking to superblock"); + close(data_device); + return -1; + } + + if (read(data_device, &sb, sizeof(sb)) != sizeof(sb)) { + ERROR("Error reading superblock"); + close(data_device); + return -1; + } + + ext4_parse_sb(&sb); + *device_size = info.len; + + close(data_device); + return 0; +} + +static int read_verity_metadata(char *block_device, char **signature, char **table) +{ + unsigned magic_number; + unsigned table_length; + uint64_t device_length; + int protocol_version; + FILE *device; + int retval = -1; + + device = fopen(block_device, "r"); + if (!device) { + ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno)); + goto out; + } + + // find the start of the verity metadata + if (get_target_device_size(block_device, &device_length) < 0) { + ERROR("Could not get target device size.\n"); + goto out; + } + if (fseek(device, device_length, SEEK_SET) < 0) { + ERROR("Could not seek to start of verity metadata block.\n"); + goto out; + } + + // check the magic number + if (!fread(&magic_number, sizeof(int), 1, device)) { + ERROR("Couldn't read magic number!\n"); + goto out; + } + if (magic_number != VERITY_METADATA_MAGIC_NUMBER) { + ERROR("Couldn't find verity metadata at offset %llu!\n", device_length); + goto out; + } + + // check the protocol version + if (!fread(&protocol_version, sizeof(int), 1, device)) { + ERROR("Couldn't read verity metadata protocol version!\n"); + goto out; + } + if (protocol_version != 0) { + ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version); + goto out; + } + + // get the signature + *signature = (char*) malloc(RSANUMBYTES * sizeof(char)); + if (!*signature) { + ERROR("Couldn't allocate memory for signature!\n"); + goto out; + } + if (!fread(*signature, RSANUMBYTES, 1, device)) { + ERROR("Couldn't read signature from verity metadata!\n"); + free(*signature); + goto out; + } + + // get the size of the table + if (!fread(&table_length, sizeof(int), 1, device)) { + ERROR("Couldn't get the size of the verity table from metadata!\n"); + free(*signature); + goto out; + } + + // get the table + null terminator + table_length += 1; + *table = malloc(table_length); + if(!*table) { + ERROR("Couldn't allocate memory for verity table!\n"); + goto out; + } + if (!fgets(*table, table_length, device)) { + ERROR("Couldn't read the verity table from metadata!\n"); + free(*table); + free(*signature); + goto out; + } + + retval = 0; + +out: + if (device) + fclose(device); + return retval; +} + +static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags) +{ + memset(io, 0, DM_BUF_SIZE); + io->data_size = DM_BUF_SIZE; + io->data_start = sizeof(struct dm_ioctl); + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + io->flags = flags | DM_READONLY_FLAG; + if (name) { + strlcpy(io->name, name, sizeof(io->name)); + } +} + +static int create_verity_device(struct dm_ioctl *io, char *name, int fd) +{ + verity_ioctl_init(io, name, 1); + if (ioctl(fd, DM_DEV_CREATE, io)) { + ERROR("Error creating device mapping (%s)", strerror(errno)); + return -1; + } + return 0; +} + +static int get_verity_device_name(struct dm_ioctl *io, char *name, int fd, char **dev_name) +{ + verity_ioctl_init(io, name, 0); + if (ioctl(fd, DM_DEV_STATUS, io)) { + ERROR("Error fetching verity device number (%s)", strerror(errno)); + return -1; + } + int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00); + if (asprintf(dev_name, "/dev/block/dm-%u", dev_num) < 0) { + ERROR("Error getting verity block device name (%s)", strerror(errno)); + return -1; + } + return 0; +} + +static int load_verity_table(struct dm_ioctl *io, char *name, char *blockdev, int fd, char *table) +{ + char *verity_params; + char *buffer = (char*) io; + uint64_t device_size = 0; + + if (get_target_device_size(blockdev, &device_size) < 0) { + return -1; + } + + verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG); + + struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + // set tgt arguments here + io->target_count = 1; + tgt->status=0; + tgt->sector_start=0; + tgt->length=device_size/512; + strcpy(tgt->target_type, "verity"); + + // build the verity params here + verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + if (sprintf(verity_params, "%s", table) < 0) { + return -1; + } + + // set next target boundary + verity_params += strlen(verity_params) + 1; + verity_params = (char*) (((unsigned long)verity_params + 7) & ~8); + tgt->next = verity_params - buffer; + + // send the ioctl to load the verity table + if (ioctl(fd, DM_TABLE_LOAD, io)) { + ERROR("Error loading verity table (%s)", strerror(errno)); + return -1; + } + + return 0; +} + +static int resume_verity_table(struct dm_ioctl *io, char *name, int fd) +{ + verity_ioctl_init(io, name, 0); + if (ioctl(fd, DM_DEV_SUSPEND, io)) { + ERROR("Error activating verity device (%s)", strerror(errno)); + return -1; + } + return 0; +} + +static int test_access(char *device) { + int tries = 25; + while (tries--) { + if (!access(device, F_OK) || errno != ENOENT) { + return 0; + } + usleep(40 * 1000); + } + return -1; +} + +int fs_mgr_setup_verity(struct fstab_rec *fstab) { + + int retval = -1; + + char *verity_blk_name; + char *verity_table; + char *verity_table_signature; + + char buffer[DM_BUF_SIZE]; + struct dm_ioctl *io = (struct dm_ioctl *) buffer; + char *mount_point = basename(fstab->mount_point); + + // set the dm_ioctl flags + io->flags |= 1; + io->target_count = 1; + + // get the device mapper fd + int fd; + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) { + ERROR("Error opening device mapper (%s)", strerror(errno)); + return retval; + } + + // create the device + if (create_verity_device(io, mount_point, fd) < 0) { + ERROR("Couldn't create verity device!"); + goto out; + } + + // get the name of the device file + if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) { + ERROR("Couldn't get verity device number!"); + goto out; + } + + // read the verity block at the end of the block device + if (read_verity_metadata(fstab->blk_device, + &verity_table_signature, + &verity_table) < 0) { + goto out; + } + + // verify the signature on the table + if (verify_table(verity_table_signature, + verity_table, + strlen(verity_table)) < 0) { + goto out; + } + + // load the verity mapping table + if (load_verity_table(io, mount_point, fstab->blk_device, fd, verity_table) < 0) { + goto out; + } + + // activate the device + if (resume_verity_table(io, mount_point, fd) < 0) { + goto out; + } + + // assign the new verity block device as the block device + free(fstab->blk_device); + fstab->blk_device = verity_blk_name; + + // make sure we've set everything up properly + if (test_access(fstab->blk_device) < 0) { + goto out; + } + + retval = 0; + +out: + close(fd); + return retval; +} diff --git a/crypto/fs_mgr/include/fs_mgr.h b/crypto/fs_mgr/include/fs_mgr.h new file mode 100644 index 000000000..0f90c32f1 --- /dev/null +++ b/crypto/fs_mgr/include/fs_mgr.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef __CORE_FS_MGR_H +#define __CORE_FS_MGR_H + +#include <stdint.h> +#include <linux/dm-ioctl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct fstab { + int num_entries; + struct fstab_rec *recs; + char *fstab_filename; +}; + +struct fstab_rec { + char *blk_device; + char *mount_point; + char *fs_type; + unsigned long flags; + char *fs_options; + int fs_mgr_flags; + char *key_loc; + char *verity_loc; + long long length; + char *label; + int partnum; + int swap_prio; + unsigned int zram_size; +}; + +struct fstab *fs_mgr_read_fstab(const char *fstab_path); +void fs_mgr_free_fstab(struct fstab *fstab); +int fs_mgr_mount_all(struct fstab *fstab); +int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, + char *tmp_mount_point); +int fs_mgr_do_tmpfs_mount(char *n_name); +int fs_mgr_unmount_all(struct fstab *fstab); +int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, + char *real_blk_device, int size); +int fs_mgr_add_entry(struct fstab *fstab, + const char *mount_point, const char *fs_type, + const char *blk_device, long long length); +struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path); +int fs_mgr_is_voldmanaged(struct fstab_rec *fstab); +int fs_mgr_is_nonremovable(struct fstab_rec *fstab); +int fs_mgr_is_encryptable(struct fstab_rec *fstab); +int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab); +int fs_mgr_swapon_all(struct fstab *fstab); +#ifdef __cplusplus +} +#endif + +#endif /* __CORE_FS_MGR_H */ + diff --git a/crypto/ics/Android.mk b/crypto/ics/Android.mk new file mode 100644 index 000000000..5616c195a --- /dev/null +++ b/crypto/ics/Android.mk @@ -0,0 +1,25 @@ +LOCAL_PATH := $(call my-dir) +ifeq ($(TW_INCLUDE_CRYPTO), true) +include $(CLEAR_VARS) + +LOCAL_MODULE := libcryptfsics +LOCAL_MODULE_TAGS := eng optional +LOCAL_CFLAGS := +LOCAL_CFLAGS += -DCRYPTO_FS_TYPE=\"$(TW_CRYPTO_FS_TYPE)\" +ifeq ($(TW_INCLUDE_CRYPTO_SAMSUNG), true) + LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO_SAMSUNG=\"$(TW_INCLUDE_CRYPTO_SAMSUNG)\" + LOCAL_LDFLAGS += -ldl + LOCAL_STATIC_LIBRARIES += libcrypt_samsung +endif +ifneq ($(TW_INTERNAL_STORAGE_PATH),) + LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_PATH=$(TW_INTERNAL_STORAGE_PATH) +endif +ifneq ($(TW_EXTERNAL_STORAGE_PATH),) + LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_PATH=$(TW_EXTERNAL_STORAGE_PATH) +endif +LOCAL_SRC_FILES = cryptfs.c +LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include +LOCAL_SHARED_LIBRARIES += libc liblog libcutils libcrypto + +include $(BUILD_SHARED_LIBRARY) +endif diff --git a/crypto/ics/cryptfs.c b/crypto/ics/cryptfs.c new file mode 100644 index 000000000..4f3d5d01a --- /dev/null +++ b/crypto/ics/cryptfs.c @@ -0,0 +1,729 @@ +/* + * Copyright (C) 2010 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. + */ + +/* TO DO: + * 1. Perhaps keep several copies of the encrypted key, in case something + * goes horribly wrong? + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <linux/dm-ioctl.h> +#include <libgen.h> +#include <stdlib.h> +#include <sys/param.h> +#include <string.h> +#include <sys/mount.h> +#include <openssl/evp.h> +#include <openssl/sha.h> +#include <errno.h> +#include <cutils/android_reboot.h> +#include <ext4.h> +#include <linux/kdev_t.h> +#include "cryptfs.h" +#define LOG_TAG "Cryptfs" +#include "cutils/log.h" +#include "cutils/properties.h" +#include "hardware_legacy/power.h" +//#include "VolumeManager.h" + +#define DM_CRYPT_BUF_SIZE 4096 +#define DATA_MNT_POINT "/data" + +#define HASH_COUNT 2000 +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG +#define KEY_LEN_BYTES_SAMSUNG (sizeof(edk_t)) +#endif +#define KEY_LEN_BYTES 16 +#define IV_LEN_BYTES 16 + +#define KEY_LOC_PROP "ro.crypto.keyfile.userdata" +#define KEY_IN_FOOTER "footer" + +#define EXT4_FS 1 +#define FAT_FS 2 + +#ifndef EXPAND +#define STRINGIFY(x) #x +#define EXPAND(x) STRINGIFY(x) +#endif + +char *me = "cryptfs"; + +static char *saved_data_blkdev; +static char *saved_mount_point; +static int master_key_saved = 0; +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG +static int using_samsung_encryption = 0; +//static edk_t saved_master_key; +static unsigned char saved_master_key[KEY_LEN_BYTES_SAMSUNG]; +edk_payload_t edk_payload; +#else +static unsigned char saved_master_key[KEY_LEN_BYTES]; +#endif + +int cryptfs_setup_volume(const char *label, const char *real_blkdev, char *crypto_blkdev); + + +static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags) +{ + memset(io, 0, dataSize); + io->data_size = dataSize; + io->data_start = sizeof(struct dm_ioctl); + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + io->flags = flags; + if (name) { + strncpy(io->name, name, sizeof(io->name)); + } +} + +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; +} + +/* key or salt can be NULL, in which case just skip writing that value. Useful to + * update the failed mount count but not change the key. + */ +static int put_crypt_ftr_and_key(char *real_blk_name, struct crypt_mnt_ftr *crypt_ftr, + unsigned char *key, unsigned char *salt) +{ + // we don't need to update it... + return 0; +} + +static int get_crypt_ftr_and_key(char *real_blk_name, struct crypt_mnt_ftr *crypt_ftr, + unsigned char *key, unsigned char *salt) +{ + int fd; + unsigned int nr_sec, cnt; + off64_t off; + int rc = -1; + char key_loc[PROPERTY_VALUE_MAX]; + char *fname; + struct stat statbuf; + + property_get(KEY_LOC_PROP, key_loc, KEY_IN_FOOTER); + + if (!strcmp(key_loc, KEY_IN_FOOTER)) { + fname = real_blk_name; + if ( (fd = open(fname, O_RDONLY)) < 0) { + printf("Cannot open real block device %s\n", fname); + return -1; + } + + if ( (nr_sec = get_blkdev_size(fd)) == 0) { + SLOGE("Cannot get size of block device %s\n", fname); + goto errout; + } + + /* If it's an encrypted Android partition, the last 16 Kbytes contain the + * encryption info footer and key, and plenty of bytes to spare for future + * growth. + */ + off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET; + + if (lseek64(fd, off, SEEK_SET) == -1) { + printf("Cannot seek to real block device footer\n"); + goto errout; + } + } else if (key_loc[0] == '/') { + fname = key_loc; + if ( (fd = open(fname, O_RDONLY)) < 0) { + printf("Cannot open footer file %s\n", fname); + return -1; + } + + /* Make sure it's 16 Kbytes in length */ + fstat(fd, &statbuf); + if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000 +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG + && statbuf.st_size != 0x8000 +#endif + )) { + printf("footer file %s is not the expected size!\n", fname); + goto errout; + } + } else { + printf("Unexpected value for" KEY_LOC_PROP "\n"); + return -1;; + } + + if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) { + printf("Cannot read real block device footer\n"); + goto errout; + } + + if (crypt_ftr->magic != CRYPT_MNT_MAGIC) { +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG + if (crypt_ftr->magic != CRYPT_MNT_MAGIC_SAMSUNG) { + printf("Bad magic for real block device %s\n", fname); + goto errout; + } else { + printf("Using Samsung encryption.\n"); + using_samsung_encryption = 1; + if ( (cnt = read(fd, &edk_payload, sizeof(edk_payload_t))) != sizeof(edk_payload_t)) { + printf("Cannot read EDK payload from real block device footer\n"); + goto errout; + } + if (lseek64(fd, sizeof(__le32), SEEK_CUR) == -1) { + printf("Cannot seek past unknown data from real block device footer\n"); + goto errout; + } + memcpy(key, &edk_payload, sizeof(edk_payload_t)); + } +#else + printf("Bad magic for real block device %s\n", fname); + goto errout; +#endif + } + + if (crypt_ftr->major_version != 1) { + printf("Cannot understand major version %d real block device footer\n", + crypt_ftr->major_version); + goto errout; + } + + if (crypt_ftr->minor_version != 0) { + printf("Warning: crypto footer minor version %d, expected 0, continuing...\n", + crypt_ftr->minor_version); + } + + if (crypt_ftr->ftr_size > sizeof(struct crypt_mnt_ftr)) { + /* the footer size is bigger than we expected. + * Skip to it's stated end so we can read the key. + */ + if (lseek64(fd, crypt_ftr->ftr_size - sizeof(struct crypt_mnt_ftr), SEEK_CUR) == -1) { + printf("Cannot seek to start of key\n"); + goto errout; + } + } + + if (crypt_ftr->keysize > sizeof(saved_master_key)) { + printf("Keysize of %d bits not supported for real block device %s\n", + crypt_ftr->keysize * 8, fname); + goto errout; + } + + if ( (cnt = read(fd, key, crypt_ftr->keysize)) != crypt_ftr->keysize) { + printf("Cannot read key for real block device %s\n", fname); + goto errout; + } + + if (lseek64(fd, KEY_TO_SALT_PADDING, SEEK_CUR) == -1) { + printf("Cannot seek to real block device salt\n"); + goto errout; + } + + if ( (cnt = read(fd, salt, SALT_LEN)) != SALT_LEN) { + printf("Cannot read salt for real block device %s\n", fname); + goto errout; + } + + /* Success! */ + rc = 0; + +errout: + close(fd); + return rc; +} + +/* Convert a binary key of specified length into an ascii hex string equivalent, + * without the leading 0x and with null termination + */ +void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize, + char *master_key_ascii) +{ + unsigned int i, a; + unsigned char nibble; + + for (i=0, a=0; i<keysize; i++, a+=2) { + /* For each byte, write out two ascii hex digits */ + nibble = (master_key[i] >> 4) & 0xf; + master_key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30); + + nibble = master_key[i] & 0xf; + master_key_ascii[a+1] = nibble + (nibble > 9 ? 0x37 : 0x30); + } + + /* Add the null termination */ + master_key_ascii[a] = '\0'; + +} + +static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key, + const char *real_blk_name, char *crypto_blk_name, const char *name) +{ + char buffer[DM_CRYPT_BUF_SIZE]; + char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */ + char *crypt_params; + struct dm_ioctl *io; + struct dm_target_spec *tgt; + unsigned int minor; + int fd; + int retval = -1; + + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) { + printf("Cannot open device-mapper\n"); + goto errout; + } + + io = (struct dm_ioctl *) buffer; + + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + if (ioctl(fd, DM_DEV_CREATE, io)) { + printf("Cannot create dm-crypt device\n"); + goto errout; + } + + /* Get the device status, in particular, the name of it's device file */ + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + if (ioctl(fd, DM_DEV_STATUS, io)) { + printf("Cannot retrieve dm-crypt device status\n"); + goto errout; + } + minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00); + snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor); + + /* Load the mapping table for this device */ + tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + ioctl_init(io, 4096, name, 0); + io->target_count = 1; + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = crypt_ftr->fs_size; + strcpy(tgt->target_type, "crypt"); + + crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii); + sprintf(crypt_params, "%s %s 0 %s 0", crypt_ftr->crypto_type_name, + master_key_ascii, real_blk_name); + //printf("cryptsetup params: '%s'\n", crypt_params); + crypt_params += strlen(crypt_params) + 1; + crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */ + tgt->next = crypt_params - buffer; + + if (ioctl(fd, DM_TABLE_LOAD, io)) { + printf("Cannot load dm-crypt mapping table.\n"); + goto errout; + } + + /* Resume this device to activate it */ + ioctl_init(io, 4096, name, 0); + + if (ioctl(fd, DM_DEV_SUSPEND, io)) { + printf("Cannot resume the dm-crypt device\n"); + goto errout; + } + + /* We made it here with no errors. Woot! */ + retval = 0; + +errout: + close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */ + + return retval; +} + +static int delete_crypto_blk_dev(const char *name) +{ + int fd; + char buffer[DM_CRYPT_BUF_SIZE]; + struct dm_ioctl *io; + int retval = -1; + + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) { + printf("Cannot open device-mapper\n"); + goto errout; + } + + io = (struct dm_ioctl *) buffer; + + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + if (ioctl(fd, DM_DEV_REMOVE, io)) { + printf("Cannot remove dm-crypt device\n"); + goto errout; + } + + /* We made it here with no errors. Woot! */ + retval = 0; + +errout: + close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */ + + return retval; + +} + +static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey) +{ + /* 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 int decrypt_master_key(char *passwd, unsigned char *salt, + unsigned char *encrypted_master_key, + unsigned char *decrypted_master_key) +{ +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG + if (using_samsung_encryption) { + property_set("rw.km_fips_status", "ready"); + return decrypt_EDK((dek_t*)decrypted_master_key, (edk_payload_t*)encrypted_master_key, passwd); + } +#endif + + 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); + + /* Initialize the decryption engine */ + if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) { + return -1; + } + EVP_CIPHER_CTX_set_padding(&d_ctx, 0); /* Turn off padding as our data is block aligned */ + /* Decrypt the master key */ + if (! EVP_DecryptUpdate(&d_ctx, decrypted_master_key, &decrypted_len, + encrypted_master_key, KEY_LEN_BYTES)) { + return -1; + } + if (! EVP_DecryptFinal(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) { + return -1; + } + + if (decrypted_len + final_len != KEY_LEN_BYTES) { + return -1; + } else { + return 0; + } +} + +static int get_orig_mount_parms( + const char *mount_point, char *fs_type, char *real_blkdev, + unsigned long *mnt_flags, char *fs_options) +{ + char mount_point2[PROPERTY_VALUE_MAX]; + char fs_flags[PROPERTY_VALUE_MAX]; + + property_get("ro.crypto.fs_type", fs_type, ""); + property_get("ro.crypto.fs_real_blkdev", real_blkdev, ""); + property_get("ro.crypto.fs_mnt_point", mount_point2, ""); + property_get("ro.crypto.fs_options", fs_options, ""); + property_get("ro.crypto.fs_flags", fs_flags, ""); + *mnt_flags = strtol(fs_flags, 0, 0); + + if (strcmp(mount_point, mount_point2)) { + /* Consistency check. These should match. If not, something odd happened. */ + return -1; + } + + return 0; +} + +static int get_orig_mount_parms_sd( + const char *mount_point, char *fs_type, char *real_blkdev) +{ + char mount_point2[PROPERTY_VALUE_MAX]; + + property_get("ro.crypto.sd_fs_type", fs_type, ""); + property_get("ro.crypto.sd_fs_real_blkdev", real_blkdev, ""); + property_get("ro.crypto.sd_fs_mnt_point", mount_point2, ""); + + if (strcmp(mount_point, mount_point2)) { + /* Consistency check. These should match. If not, something odd happened. */ + return -1; + } + + return 0; +} + +static int test_mount_encrypted_fs( + char *passwd, char *mount_point, char *label, char *crypto_blkdev) +{ + struct crypt_mnt_ftr crypt_ftr; + /* Allocate enough space for a 256 bit key, but we may use less */ + unsigned char encrypted_master_key[256], decrypted_master_key[32]; + unsigned char salt[SALT_LEN]; + char real_blkdev[MAXPATHLEN]; + char fs_type[PROPERTY_VALUE_MAX]; + char fs_options[PROPERTY_VALUE_MAX]; + char tmp_mount_point[MAXPATHLEN]; + unsigned long mnt_flags; + unsigned int orig_failed_decrypt_count; + char encrypted_state[PROPERTY_VALUE_MAX]; + int rc; + + property_get("ro.crypto.state", encrypted_state, ""); + if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) { + printf("encrypted fs already validated or not running with encryption, aborting %s\n", encrypted_state); + return -1; + } + + if (get_orig_mount_parms(mount_point, fs_type, real_blkdev, &mnt_flags, fs_options)) { + printf("Error reading original mount parms for mount point %s\n", mount_point); + return -1; + } + + if (get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt)) { + printf("Error getting crypt footer and key\n"); + return -1; + } + + //printf("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) ) { + decrypt_master_key(passwd, salt, encrypted_master_key, decrypted_master_key); + } + + if (create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, + crypto_blkdev, label)) { + printf("Error creating decrypted block device\n"); + return -1; + } + + /* If init detects an encrypted filesystme, it writes a file for each such + * encrypted fs into the tmpfs /data filesystem, and then the framework finds those + * files and passes that data to me */ + /* Create a tmp mount point to try mounting the decryptd fs + * Since we're here, the mount_point should be a tmpfs filesystem, so make + * a directory in it to test mount the decrypted filesystem. + */ + sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point); + mkdir(tmp_mount_point, 0755); + if ( mount(crypto_blkdev, tmp_mount_point, fs_type, MS_RDONLY, "") ) { + printf("Error temp mounting decrypted block device\n"); + delete_crypto_blk_dev(label); + crypt_ftr.failed_decrypt_count++; + } else { + /* Success, so just umount and we'll mount it properly when we restart + * the framework. + */ + umount(tmp_mount_point); + crypt_ftr.failed_decrypt_count = 0; + } + + rmdir(tmp_mount_point); + + 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 + * so we can mount it when restarting the framework. + */ + property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev); + + /* Also save a the master key so we can reencrypted the key + * the key when we want to change the password on it. + */ + memcpy(saved_master_key, decrypted_master_key, sizeof(saved_master_key)); + saved_data_blkdev = strdup(real_blkdev); + saved_mount_point = strdup(mount_point); + master_key_saved = 1; + rc = 0; + } + + return rc; +} + +static int test_mount_encrypted_fs_sd( + const char *passwd, const char *mount_point, const char *label) +{ + char real_blkdev[MAXPATHLEN]; + char crypto_blkdev[MAXPATHLEN]; + char tmp_mount_point[MAXPATHLEN]; + char encrypted_state[PROPERTY_VALUE_MAX]; + char fs_type[PROPERTY_VALUE_MAX]; + int rc; + + property_get("ro.crypto.state", encrypted_state, ""); + if ( !master_key_saved || strcmp(encrypted_state, "encrypted") ) { + printf("encrypted fs not yet validated or not running with encryption, aborting\n"); + return -1; + } + + if (get_orig_mount_parms_sd(mount_point, fs_type, real_blkdev)) { + printf("Error reading original mount parms for mount point %s\n", mount_point); + return -1; + } + + rc = cryptfs_setup_volume(label, real_blkdev, crypto_blkdev); + if(rc){ + printf("Error setting up cryptfs volume %s\n", real_blkdev); + return -1; + } + + sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point); + mkdir(tmp_mount_point, 0755); + if ( mount(crypto_blkdev, tmp_mount_point, fs_type, MS_RDONLY, "") ) { + printf("Error temp mounting decrypted block device\n"); + delete_crypto_blk_dev(label); + } else { + /* Success, so just umount and we'll mount it properly when we restart + * the framework. + */ + umount(tmp_mount_point); + + property_set("ro.crypto.sd_fs_crypto_blkdev", crypto_blkdev); + } + + rmdir(tmp_mount_point); + + return rc; +} + +/* + * Called by vold when it's asked to mount an encrypted, nonremovable volume. + * Setup a dm-crypt mapping, use the saved master key from + * setting up the /data mapping, and return the new device path. + */ +int cryptfs_setup_volume(const char *label, const char *real_blkdev, char *crypto_blkdev) +{ + struct crypt_mnt_ftr sd_crypt_ftr; + unsigned char key[256], salt[32]; + struct stat statbuf; + int nr_sec, fd, rc; + + /* Just want the footer, but gotta get it all */ + get_crypt_ftr_and_key(saved_data_blkdev, &sd_crypt_ftr, key, salt); + + /* Update the fs_size field to be the size of the volume */ + fd = open(real_blkdev, O_RDONLY); + nr_sec = get_blkdev_size(fd); + close(fd); + if (nr_sec == 0) { + SLOGE("Cannot get size of volume %s\n", real_blkdev); + return -1; + } + +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG + if(using_samsung_encryption) { + if(!access("/efs/essiv", R_OK)){ + strcpy(sd_crypt_ftr.crypto_type_name, "aes-cbc-plain:sha1"); + } + else if(!access("/efs/cryptprop_essiv", R_OK)){ + strcpy(sd_crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256"); + } + } +#endif + + sd_crypt_ftr.fs_size = nr_sec; + rc = create_crypto_blk_dev( + &sd_crypt_ftr, saved_master_key, real_blkdev, crypto_blkdev, label); + + stat(crypto_blkdev, &statbuf); + + return rc; +} + +int cryptfs_crypto_complete(void) +{ + return -1; +} + +int cryptfs_check_footer(void) +{ + int rc = -1; + char fs_type[PROPERTY_VALUE_MAX]; + char real_blkdev[MAXPATHLEN]; + char fs_options[PROPERTY_VALUE_MAX]; + unsigned long mnt_flags; + struct crypt_mnt_ftr crypt_ftr; + /* Allocate enough space for a 256 bit key, but we may use less */ + unsigned char encrypted_master_key[256]; + unsigned char salt[SALT_LEN]; + + if (get_orig_mount_parms(DATA_MNT_POINT, fs_type, real_blkdev, &mnt_flags, fs_options)) { + printf("Error reading original mount parms for mount point %s\n", DATA_MNT_POINT); + return rc; + } + + rc = get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt); + + return rc; +} + +int cryptfs_check_passwd(const char *passwd) +{ + char pwbuf[256]; + char crypto_blkdev_data[MAXPATHLEN]; + int rc = -1; + + strcpy(pwbuf, passwd); + rc = test_mount_encrypted_fs(pwbuf, DATA_MNT_POINT, "userdata", crypto_blkdev_data); + +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG + if(using_samsung_encryption) { + + int rc2 = 1; +#ifndef RECOVERY_SDCARD_ON_DATA +#ifdef TW_INTERNAL_STORAGE_PATH + // internal storage for non data/media devices + if(!rc) { + strcpy(pwbuf, passwd); + rc2 = test_mount_encrypted_fs_sd( + pwbuf, EXPAND(TW_INTERNAL_STORAGE_PATH), + EXPAND(TW_INTERNAL_STORAGE_MOUNT_POINT)); + } +#endif +#endif +#ifdef TW_EXTERNAL_STORAGE_PATH + printf("Temp mounting /data\n"); + // mount data so mount_ecryptfs_drive can access edk in /data/system/ + rc2 = mount(crypto_blkdev_data, DATA_MNT_POINT, CRYPTO_FS_TYPE, MS_RDONLY, ""); + // external sd + char decrypt_external[256], external_blkdev[256]; + property_get("ro.crypto.external_encrypted", decrypt_external, "0"); + // Mount the external storage as ecryptfs so that ecryptfs can act as a pass-through + if (!rc2 && strcmp(decrypt_external, "1") == 0) { + printf("Mounting external with ecryptfs...\n"); + strcpy(pwbuf, passwd); + rc2 = mount_ecryptfs_drive( + pwbuf, EXPAND(TW_EXTERNAL_STORAGE_PATH), + EXPAND(TW_EXTERNAL_STORAGE_PATH), 0); + if (rc2 == 0) + property_set("ro.crypto.external_use_ecryptfs", "1"); + else + property_set("ro.crypto.external_use_ecryptfs", "0"); + } else { + printf("Unable to mount external storage with ecryptfs.\n"); + umount(EXPAND(TW_EXTERNAL_STORAGE_PATH)); + } + umount(DATA_MNT_POINT); + } +#endif // #ifdef TW_EXTERNAL_STORAGE_PATH +#endif // #ifdef TW_INCLUDE_CRYPTO_SAMSUNG + return rc; +} diff --git a/crypto/ics/cryptfs.h b/crypto/ics/cryptfs.h new file mode 100644 index 000000000..8c8037659 --- /dev/null +++ b/crypto/ics/cryptfs.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010 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. + */ + +/* This structure starts 16,384 bytes before the end of a hardware + * partition that is encrypted. + * Immediately following this structure is the encrypted key. + * The keysize field tells how long the key is, in bytes. + * Then there is 32 bytes of padding, + * Finally there is the salt used with the user password. + * The salt is fixed at 16 bytes long. + * Obviously, the filesystem does not include the last 16 kbytes + * of the partition. + */ + +#ifndef __CRYPTFS_H__ +#define __CRYPTFS_H__ + +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG +#include "../libcrypt_samsung/include/libcrypt_samsung.h" +#endif + +#define CRYPT_FOOTER_OFFSET 0x4000 + +#define MAX_CRYPTO_TYPE_NAME_LEN 64 + +#define SALT_LEN 16 +#define KEY_TO_SALT_PADDING 32 + +/* definitions of flags in the structure below */ +#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */ +#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Set when starting encryption, + * clear when done before rebooting */ + +#ifdef TW_INCLUDE_CRYPTO_SAMSUNG +#define CRYPT_MNT_MAGIC_SAMSUNG 0xD0B5B1C5 +#endif +#define CRYPT_MNT_MAGIC 0xD0B5B1C4 + +#define __le32 unsigned int +#define __le16 unsigned short int + +#pragma pack(1) + +struct crypt_mnt_ftr { + __le32 magic; /* See above */ + __le16 major_version; + __le16 minor_version; + __le32 ftr_size; /* in bytes, not including key following */ + __le32 flags; /* See above */ + __le32 keysize; /* in bytes */ + __le32 spare1; /* ignored */ + __le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */ + __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and + mount, set to 0 on successful mount */ + char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption + needed to decrypt this + partition, null terminated */ +}; + +#pragma pack() + + +#ifdef __cplusplus +extern "C" { +#endif + int cryptfs_check_footer(void); + int cryptfs_check_passwd(const char *pw); +#ifdef __cplusplus +} +#endif + +#endif // __CRYPTFS_H__ + diff --git a/crypto/jb/Android.mk b/crypto/jb/Android.mk new file mode 100644 index 000000000..90321d67e --- /dev/null +++ b/crypto/jb/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH := $(call my-dir) +ifeq ($(TW_INCLUDE_JB_CRYPTO), true) +include $(CLEAR_VARS) + +LOCAL_MODULE := libcryptfsjb +LOCAL_MODULE_TAGS := eng optional +LOCAL_CFLAGS := +LOCAL_SRC_FILES = cryptfs.c +LOCAL_C_INCLUDES += \ + system/extras/ext4_utils \ + external/openssl/include \ + $(commands_recovery_local_path)/crypto/scrypt/lib/crypto +LOCAL_SHARED_LIBRARIES += libc liblog libcutils libcrypto libext4_utils +LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp libscrypttwrp_static liblogwraptwrp libmincrypttwrp + +include $(BUILD_SHARED_LIBRARY) +endif diff --git a/crypto/jb/cryptfs.c b/crypto/jb/cryptfs.c new file mode 100644 index 000000000..f9c0d7489 --- /dev/null +++ b/crypto/jb/cryptfs.c @@ -0,0 +1,1735 @@ +/* + * Copyright (C) 2010 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. + */ + +/* TO DO: + * 1. Perhaps keep several copies of the encrypted key, in case something + * goes horribly wrong? + * + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <linux/dm-ioctl.h> +#include <libgen.h> +#include <stdlib.h> +#include <sys/param.h> +#include <string.h> +#include <sys/mount.h> +#include <openssl/evp.h> +#include <openssl/sha.h> +#include <errno.h> +#include <ext4.h> +#include <linux/kdev_t.h> +#include <fs_mgr.h> +#include "cryptfs.h" +#define LOG_TAG "Cryptfs" +#include "cutils/log.h" +#include "cutils/properties.h" +#include "cutils/android_reboot.h" +#include "hardware_legacy/power.h" +/*#include <logwrap/logwrap.h> +#include "VolumeManager.h" +#include "VoldUtil.h"*/ +#include "crypto_scrypt.h" + +#define DM_CRYPT_BUF_SIZE 4096 +#define DATA_MNT_POINT "/data" + +#define HASH_COUNT 2000 +#define KEY_LEN_BYTES 16 +#define IV_LEN_BYTES 16 + +#define KEY_IN_FOOTER "footer" + +#define EXT4_FS 1 +#define FAT_FS 2 + +#define TABLE_LOAD_RETRIES 10 + +char *me = "cryptfs"; + +static unsigned char saved_master_key[KEY_LEN_BYTES]; +static char *saved_mount_point; +static int master_key_saved = 0; +static struct crypt_persist_data *persist_data = NULL; + +struct fstab *fstab; + +static void cryptfs_reboot(int recovery) +{ + /*if (recovery) { + property_set(ANDROID_RB_PROPERTY, "reboot,recovery"); + } else { + property_set(ANDROID_RB_PROPERTY, "reboot"); + } + sleep(20);*/ + + /* Shouldn't get here, reboot should happen before sleep times out */ + return; +} + +static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags) +{ + memset(io, 0, dataSize); + io->data_size = dataSize; + io->data_start = sizeof(struct dm_ioctl); + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + io->flags = flags; + if (name) { + strncpy(io->name, name, sizeof(io->name)); + } +} + +/** + * 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 (i = 0, 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)) { + printf("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; + struct ext4_super_block sb; + off64_t len; + + if ((fd = open(dev, O_RDONLY)) < 0) { + printf("Cannot open device to get filesystem size "); + return 0; + } + + if (lseek64(fd, 1024, SEEK_SET) < 0) { + printf("Cannot seek to superblock"); + return 0; + } + + if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) { + printf("Cannot read superblock"); + return 0; + } + + close(fd); + + block_size = 1024 << sb.s_log_block_size; + /* compute length in bytes */ + len = ( ((off64_t)sb.s_blocks_count_hi << 32) + sb.s_blocks_count_lo) * block_size; + + /* return length in sectors */ + 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; + static off64_t cached_off = 0; + static char cached_metadata_fname[PROPERTY_VALUE_MAX] = ""; + int fd; + char key_loc[PROPERTY_VALUE_MAX]; + char real_blkdev[PROPERTY_VALUE_MAX]; + unsigned int nr_sec; + int rc = -1; + + if (!cached_data) { + fs_mgr_get_crypt_info(fstab, key_loc, real_blkdev, sizeof(key_loc)); + + if (!strcmp(key_loc, KEY_IN_FOOTER)) { + if ( (fd = open(real_blkdev, O_RDWR)) < 0) { + printf("Cannot open real block device %s\n", real_blkdev); + return -1; + } + + if ((nr_sec = get_blkdev_size(fd))) { + /* If it's an encrypted Android partition, the last 16 Kbytes contain the + * encryption info footer and key, and plenty of bytes to spare for future + * growth. + */ + strlcpy(cached_metadata_fname, real_blkdev, sizeof(cached_metadata_fname)); + cached_off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET; + cached_data = 1; + } else { + printf("Cannot get size of block device %s\n", real_blkdev); + } + close(fd); + } else { + strlcpy(cached_metadata_fname, key_loc, sizeof(cached_metadata_fname)); + cached_off = 0; + cached_data = 1; + } + } + + if (cached_data) { + if (metadata_fname) { + *metadata_fname = cached_metadata_fname; + } + if (off) { + *off = cached_off; + } + rc = 0; + } + + return rc; +} + +/* key or salt can be NULL, in which case just skip writing that value. Useful to + * update the failed mount count but not change the key. + */ +static int put_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr) +{ + int fd; + unsigned int nr_sec, cnt; + /* starting_off is set to the SEEK_SET offset + * where the crypto structure starts + */ + off64_t starting_off; + int rc = -1; + char *fname = NULL; + struct stat statbuf; + + if (get_crypt_ftr_info(&fname, &starting_off)) { + printf("Unable to get crypt_ftr_info\n"); + return -1; + } + if (fname[0] != '/') { + printf("Unexpected value for crypto key location\n"); + return -1; + } + if ( (fd = open(fname, O_RDWR | O_CREAT, 0600)) < 0) { + printf("Cannot open footer file %s for put\n", fname); + return -1; + } + + /* Seek to the start of the crypt footer */ + if (lseek64(fd, starting_off, SEEK_SET) == -1) { + printf("Cannot seek to real block device footer\n"); + goto errout; + } + + if ((cnt = write(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) { + printf("Cannot write real block device footer\n"); + goto errout; + } + + fstat(fd, &statbuf); + /* If the keys are kept on a raw block device, do not try to truncate it. */ + if (S_ISREG(statbuf.st_mode)) { + if (ftruncate(fd, 0x4000)) { + printf("Cannot set footer file size\n", fname); + goto errout; + } + } + + /* Success! */ + rc = 0; + +errout: + close(fd); + return rc; + +} + +static inline int unix_read(int fd, void* buff, int len) +{ + return TEMP_FAILURE_RETRY(read(fd, buff, len)); +} + +static inline int unix_write(int fd, const void* buff, int len) +{ + return TEMP_FAILURE_RETRY(write(fd, buff, len)); +} + +static void init_empty_persist_data(struct crypt_persist_data *pdata, int len) +{ + memset(pdata, 0, len); + pdata->persist_magic = PERSIST_DATA_MAGIC; + pdata->persist_valid_entries = 0; +} + +/* A routine to update the passed in crypt_ftr to the lastest version. + * fd is open read/write on the device that holds the crypto footer and persistent + * data, crypt_ftr is a pointer to the struct to be updated, and offset is the + * absolute offset to the start of the crypt_mnt_ftr on the passed in fd. + */ +static void upgrade_crypt_ftr(int fd, struct crypt_mnt_ftr *crypt_ftr, off64_t offset) +{ + int orig_major = crypt_ftr->major_version; + int orig_minor = crypt_ftr->minor_version; + return; // in recovery we don't want to upgrade + 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; + + printf("upgrading crypto footer to 1.1"); + + pdata = malloc(CRYPT_PERSIST_DATA_SIZE); + if (pdata == NULL) { + printf("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) { + printf("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; + } + + if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version)) { + printf("upgrading crypto footer to 1.2"); + crypt_ftr->kdf_type = KDF_PBKDF2; + get_device_scrypt_params(crypt_ftr); + crypt_ftr->minor_version = 2; + } + + if ((orig_major != crypt_ftr->major_version) || (orig_minor != crypt_ftr->minor_version)) { + if (lseek64(fd, offset, SEEK_SET) == -1) { + printf("Cannot seek to crypt footer\n"); + return; + } + unix_write(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr)); + } +} + + +static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr) +{ + int fd; + unsigned int nr_sec, cnt; + off64_t starting_off; + int rc = -1; + char *fname = NULL; + struct stat statbuf; + + if (get_crypt_ftr_info(&fname, &starting_off)) { + printf("Unable to get crypt_ftr_info\n"); + return -1; + } + if (fname[0] != '/') { + printf("Unexpected value for crypto key location\n"); + return -1; + } + if ( (fd = open(fname, O_RDWR)) < 0) { + printf("Cannot open footer file %s for get\n", fname); + return -1; + } + + /* Make sure it's 16 Kbytes in length */ + fstat(fd, &statbuf); + if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) { + printf("footer file %s is not the expected size!\n", fname); + goto errout; + } + + /* Seek to the start of the crypt footer */ + if (lseek64(fd, starting_off, SEEK_SET) == -1) { + printf("Cannot seek to real block device footer\n"); + goto errout; + } + + if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) { + printf("Cannot read real block device footer\n"); + goto errout; + } + + if (crypt_ftr->magic != CRYPT_MNT_MAGIC) { + printf("Bad magic for real block device %s\n", fname); + goto errout; + } + + if (crypt_ftr->major_version != CURRENT_MAJOR_VERSION) { + printf("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 > CURRENT_MINOR_VERSION) { + printf("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 < CURRENT_MINOR_VERSION) { + upgrade_crypt_ftr(fd, crypt_ftr, starting_off); + }*/ + + /* Success! */ + rc = 0; + +errout: + close(fd); + return rc; +} + +static int validate_persistent_data_storage(struct crypt_mnt_ftr *crypt_ftr) +{ + if (crypt_ftr->persist_data_offset[0] + crypt_ftr->persist_data_size > + crypt_ftr->persist_data_offset[1]) { + printf("Crypt_ftr persist data regions overlap"); + return -1; + } + + if (crypt_ftr->persist_data_offset[0] >= crypt_ftr->persist_data_offset[1]) { + printf("Crypt_ftr persist data region 0 starts after region 1"); + return -1; + } + + if (((crypt_ftr->persist_data_offset[1] + crypt_ftr->persist_data_size) - + (crypt_ftr->persist_data_offset[0] - CRYPT_FOOTER_TO_PERSIST_OFFSET)) > + CRYPT_FOOTER_OFFSET) { + printf("Persistent data extends past crypto footer"); + return -1; + } + + return 0; +} + +static int load_persistent_data(void) +{ + struct crypt_mnt_ftr crypt_ftr; + struct crypt_persist_data *pdata = NULL; + char encrypted_state[PROPERTY_VALUE_MAX]; + char *fname; + int found = 0; + int fd; + int ret; + int i; + + if (persist_data) { + /* Nothing to do, we've already loaded or initialized it */ + return 0; + } + + + /* If not encrypted, just allocate an empty table and initialize it */ + property_get("ro.crypto.state", encrypted_state, ""); + if (strcmp(encrypted_state, "encrypted") ) { + pdata = malloc(CRYPT_PERSIST_DATA_SIZE); + if (pdata) { + init_empty_persist_data(pdata, CRYPT_PERSIST_DATA_SIZE); + persist_data = pdata; + return 0; + } + return -1; + } + + if(get_crypt_ftr_and_key(&crypt_ftr)) { + return -1; + } + + if ((crypt_ftr.major_version != 1) || (crypt_ftr.minor_version != 1)) { + printf("Crypt_ftr version doesn't support persistent data"); + return -1; + } + + if (get_crypt_ftr_info(&fname, NULL)) { + return -1; + } + + ret = validate_persistent_data_storage(&crypt_ftr); + if (ret) { + return -1; + } + + fd = open(fname, O_RDONLY); + if (fd < 0) { + printf("Cannot open %s metadata file", fname); + return -1; + } + + if (persist_data == NULL) { + pdata = malloc(crypt_ftr.persist_data_size); + if (pdata == NULL) { + printf("Cannot allocate memory for persistent data"); + goto err; + } + } + + for (i = 0; i < 2; i++) { + if (lseek64(fd, crypt_ftr.persist_data_offset[i], SEEK_SET) < 0) { + printf("Cannot seek to read persistent data on %s", fname); + goto err2; + } + if (unix_read(fd, pdata, crypt_ftr.persist_data_size) < 0){ + printf("Error reading persistent data on iteration %d", i); + goto err2; + } + if (pdata->persist_magic == PERSIST_DATA_MAGIC) { + found = 1; + break; + } + } + + if (!found) { + printf("Could not find valid persistent data, creating"); + init_empty_persist_data(pdata, crypt_ftr.persist_data_size); + } + + /* Success */ + persist_data = pdata; + close(fd); + return 0; + +err2: + free(pdata); + +err: + close(fd); + return -1; +} + +static int save_persistent_data(void) +{ + struct crypt_mnt_ftr crypt_ftr; + struct crypt_persist_data *pdata; + char *fname; + off64_t write_offset; + off64_t erase_offset; + int found = 0; + int fd; + int ret; + + if (persist_data == NULL) { + printf("No persistent data to save"); + return -1; + } + + if(get_crypt_ftr_and_key(&crypt_ftr)) { + return -1; + } + + if ((crypt_ftr.major_version != 1) || (crypt_ftr.minor_version != 1)) { + printf("Crypt_ftr version doesn't support persistent data"); + return -1; + } + + ret = validate_persistent_data_storage(&crypt_ftr); + if (ret) { + return -1; + } + + if (get_crypt_ftr_info(&fname, NULL)) { + return -1; + } + + fd = open(fname, O_RDWR); + if (fd < 0) { + printf("Cannot open %s metadata file", fname); + return -1; + } + + pdata = malloc(crypt_ftr.persist_data_size); + if (pdata == NULL) { + printf("Cannot allocate persistant data"); + goto err; + } + + if (lseek64(fd, crypt_ftr.persist_data_offset[0], SEEK_SET) < 0) { + printf("Cannot seek to read persistent data on %s", fname); + goto err2; + } + + if (unix_read(fd, pdata, crypt_ftr.persist_data_size) < 0) { + printf("Error reading persistent data before save"); + goto err2; + } + + if (pdata->persist_magic == PERSIST_DATA_MAGIC) { + /* The first copy is the curent valid copy, so write to + * the second copy and erase this one */ + write_offset = crypt_ftr.persist_data_offset[1]; + erase_offset = crypt_ftr.persist_data_offset[0]; + } else { + /* The second copy must be the valid copy, so write to + * the first copy, and erase the second */ + write_offset = crypt_ftr.persist_data_offset[0]; + erase_offset = crypt_ftr.persist_data_offset[1]; + } + + /* Write the new copy first, if successful, then erase the old copy */ + if (lseek(fd, write_offset, SEEK_SET) < 0) { + printf("Cannot seek to write persistent data"); + goto err2; + } + if (unix_write(fd, persist_data, crypt_ftr.persist_data_size) == + (int) crypt_ftr.persist_data_size) { + if (lseek(fd, erase_offset, SEEK_SET) < 0) { + printf("Cannot seek to erase previous persistent data"); + goto err2; + } + fsync(fd); + memset(pdata, 0, crypt_ftr.persist_data_size); + if (unix_write(fd, pdata, crypt_ftr.persist_data_size) != + (int) crypt_ftr.persist_data_size) { + printf("Cannot write to erase previous persistent data"); + goto err2; + } + fsync(fd); + } else { + printf("Cannot write to save persistent data"); + goto err2; + } + + /* Success */ + free(pdata); + close(fd); + return 0; + +err2: + free(pdata); +err: + close(fd); + return -1; +} + +/* Convert a binary key of specified length into an ascii hex string equivalent, + * without the leading 0x and with null termination + */ +void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize, + char *master_key_ascii) +{ + unsigned int i, a; + unsigned char nibble; + + for (i=0, a=0; i<keysize; i++, a+=2) { + /* For each byte, write out two ascii hex digits */ + nibble = (master_key[i] >> 4) & 0xf; + master_key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30); + + nibble = master_key[i] & 0xf; + master_key_ascii[a+1] = nibble + (nibble > 9 ? 0x37 : 0x30); + } + + /* Add the null termination */ + master_key_ascii[a] = '\0'; + +} + +static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key, + char *real_blk_name, const char *name, int fd, + char *extra_params) +{ + char buffer[DM_CRYPT_BUF_SIZE]; + struct dm_ioctl *io; + struct dm_target_spec *tgt; + char *crypt_params; + char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */ + int i; + + io = (struct dm_ioctl *) buffer; + + /* Load the mapping table for this device */ + tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + io->target_count = 1; + tgt->status = 0; + tgt->sector_start = 0; + tgt->length = crypt_ftr->fs_size; + strcpy(tgt->target_type, "crypt"); + + crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii); + sprintf(crypt_params, "%s %s 0 %s 0 %s", crypt_ftr->crypto_type_name, + master_key_ascii, real_blk_name, extra_params); + crypt_params += strlen(crypt_params) + 1; + crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */ + tgt->next = crypt_params - buffer; + + for (i = 0; i < TABLE_LOAD_RETRIES; i++) { + if (! ioctl(fd, DM_TABLE_LOAD, io)) { + break; + } + usleep(500000); + } + + if (i == TABLE_LOAD_RETRIES) { + /* We failed to load the table, return an error */ + return -1; + } else { + return i + 1; + } +} + + +static int get_dm_crypt_version(int fd, const char *name, int *version) +{ + char buffer[DM_CRYPT_BUF_SIZE]; + struct dm_ioctl *io; + struct dm_target_versions *v; + int i; + + io = (struct dm_ioctl *) buffer; + + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + + if (ioctl(fd, DM_LIST_VERSIONS, io)) { + return -1; + } + + /* Iterate over the returned versions, looking for name of "crypt". + * When found, get and return the version. + */ + v = (struct dm_target_versions *) &buffer[sizeof(struct dm_ioctl)]; + while (v->next) { + if (! strcmp(v->name, "crypt")) { + /* We found the crypt driver, return the version, and get out */ + version[0] = v->version[0]; + version[1] = v->version[1]; + version[2] = v->version[2]; + return 0; + } + v = (struct dm_target_versions *)(((char *)v) + v->next); + } + + return -1; +} + +static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key, + char *real_blk_name, char *crypto_blk_name, const char *name) +{ + char buffer[DM_CRYPT_BUF_SIZE]; + char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */ + char *crypt_params; + struct dm_ioctl *io; + struct dm_target_spec *tgt; + unsigned int minor; + int fd; + int i; + int retval = -1; + int version[3]; + char *extra_params; + int load_count; + + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) { + printf("Cannot open device-mapper\n"); + goto errout; + } + + io = (struct dm_ioctl *) buffer; + + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + if (ioctl(fd, DM_DEV_CREATE, io)) { + printf("Cannot create dm-crypt device\n"); + goto errout; + } + + /* Get the device status, in particular, the name of it's device file */ + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + if (ioctl(fd, DM_DEV_STATUS, io)) { + printf("Cannot retrieve dm-crypt device status\n"); + goto errout; + } + minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00); + snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor); + + extra_params = ""; + if (! get_dm_crypt_version(fd, name, version)) { + /* Support for allow_discards was added in version 1.11.0 */ + if ((version[0] >= 2) || + ((version[0] == 1) && (version[1] >= 11))) { + extra_params = "1 allow_discards"; + printf("Enabling support for allow_discards in dmcrypt.\n"); + } + } + + load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, + fd, extra_params); + if (load_count < 0) { + printf("Cannot load dm-crypt mapping table.\n"); + goto errout; + } else if (load_count > 1) { + printf("Took %d tries to load dmcrypt table.\n", load_count); + } + + /* Resume this device to activate it */ + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + + if (ioctl(fd, DM_DEV_SUSPEND, io)) { + printf("Cannot resume the dm-crypt device\n"); + goto errout; + } + + /* We made it here with no errors. Woot! */ + retval = 0; + +errout: + close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */ + + return retval; +} + +static int delete_crypto_blk_dev(char *name) +{ + int fd; + char buffer[DM_CRYPT_BUF_SIZE]; + struct dm_ioctl *io; + int retval = -1; + + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) { + printf("Cannot open device-mapper\n"); + goto errout; + } + + io = (struct dm_ioctl *) buffer; + + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); + if (ioctl(fd, DM_DEV_REMOVE, io)) { + printf("Cannot remove dm-crypt device\n"); + goto errout; + } + + /* We made it here with no errors. Woot! */ + retval = 0; + +errout: + close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */ + + return retval; + +} + +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, + 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 */ + 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)) { + printf("EVP_EncryptInit failed\n"); + 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, encrypted_master_key, &encrypted_len, + decrypted_master_key, KEY_LEN_BYTES)) { + printf("EVP_EncryptUpdate failed\n"); + return -1; + } + if (! EVP_EncryptFinal(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) { + printf("EVP_EncryptFinal failed\n"); + return -1; + } + + if (encrypted_len + final_len != KEY_LEN_BYTES) { + printf("EVP_Encryption length check failed with %d, %d bytes\n", encrypted_len, final_len); + return -1; + } else { + return 0; + } +} + +static int decrypt_master_key(char *passwd, unsigned char *salt, + unsigned char *encrypted_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 */ + kdf(passwd, salt, ikey, kdf_params); + + /* Initialize the decryption engine */ + if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) { + return -1; + } + EVP_CIPHER_CTX_set_padding(&d_ctx, 0); /* Turn off padding as our data is block aligned */ + /* Decrypt the master key */ + if (! EVP_DecryptUpdate(&d_ctx, decrypted_master_key, &decrypted_len, + encrypted_master_key, KEY_LEN_BYTES)) { + return -1; + } + if (! EVP_DecryptFinal(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) { + return -1; + } + + if (decrypted_len + final_len != KEY_LEN_BYTES) { + return -1; + } else { + return 0; + } +} + +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) { + printf("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; + int encrypted_len, final_len; + + /* Get some random bits for a key */ + fd = open("/dev/urandom", O_RDONLY); + read(fd, key_buf, sizeof(key_buf)); + read(fd, salt, SALT_LEN); + close(fd); + + /* Now encrypt it with the password */ + return encrypt_master_key(passwd, salt, key_buf, master_key, crypt_ftr); +} + +static int wait_and_unmount(char *mountpoint) +{ + int i, rc; +#define WAIT_UNMOUNT_COUNT 20 + + /* Now umount the tmpfs filesystem */ + for (i=0; i<WAIT_UNMOUNT_COUNT; i++) { + if (umount(mountpoint)) { + if (errno == EINVAL) { + /* EINVAL is returned if the directory is not a mountpoint, + * i.e. there is no filesystem mounted there. So just get out. + */ + break; + } + sleep(1); + i++; + } else { + break; + } + } + + if (i < WAIT_UNMOUNT_COUNT) { + printf("unmounting %s succeeded\n", mountpoint); + rc = 0; + } else { + printf("unmounting %s failed\n", mountpoint); + rc = -1; + } + + return rc; +} + +#define DATA_PREP_TIMEOUT 200 +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"); + printf("Just triggered post_fs_data\n"); + + /* Wait a max of 50 seconds, hopefully it takes much less */ + for (i=0; i<DATA_PREP_TIMEOUT; i++) { + char p[PROPERTY_VALUE_MAX]; + + property_get("vold.post_fs_data_done", p, "0"); + if (*p == '1') { + break; + } else { + usleep(250000); + } + } + if (i == DATA_PREP_TIMEOUT) { + /* Ugh, we failed to prep /data in time. Bail. */ + printf("post_fs_data timed out!\n"); + return -1; + } else { + printf("post_fs_data done\n"); + return 0; + } +} + +int cryptfs_restart(void) +{ + char fs_type[32]; + char real_blkdev[MAXPATHLEN]; + char crypto_blkdev[MAXPATHLEN]; + char fs_options[256]; + unsigned long mnt_flags; + struct stat statbuf; + int rc = -1, i; + static int restart_successful = 0; + + /* Validate that it's OK to call this routine */ + if (! master_key_saved) { + printf("Encrypted filesystem not validated, aborting"); + return -1; + } + + if (restart_successful) { + printf("System already restarted with encrypted disk, aborting"); + return -1; + } + + /* Here is where we shut down the framework. The init scripts + * start all services in one of three classes: core, main or late_start. + * On boot, we start core and main. Now, we stop main, but not core, + * as core includes vold and a few other really important things that + * we need to keep running. Once main has stopped, we should be able + * to umount the tmpfs /data, then mount the encrypted /data. + * We then restart the class main, and also the class late_start. + * At the moment, I've only put a few things in late_start that I know + * are not needed to bring up the framework, and that also cause problems + * with unmounting the tmpfs /data, but I hope to add add more services + * to the late_start class as we optimize this to decrease the delay + * till the user is asked for the password to the filesystem. + */ + + /* The init files are setup to stop the class main when vold.decrypt is + * set to trigger_reset_main. + */ + property_set("vold.decrypt", "trigger_reset_main"); + printf("Just asked init to shut down class main\n"); + + /* Ugh, shutting down the framework is not synchronous, so until it + * can be fixed, this horrible hack will wait a moment for it all to + * shut down before proceeding. Without it, some devices cannot + * restart the graphics services. + */ + sleep(2); + + /* Now that the framework is shutdown, we should be able to umount() + * the tmpfs filesystem, and mount the real one. + */ + + property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, ""); + if (strlen(crypto_blkdev) == 0) { + printf("fs_crypto_blkdev not set\n"); + return -1; + } + + if (! (rc = wait_and_unmount(DATA_MNT_POINT)) ) { + /* If that succeeded, then mount the decrypted filesystem */ + fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, 0); + + property_set("vold.decrypt", "trigger_load_persist_props"); + /* Create necessary paths on /data */ + if (prep_data_fs()) { + return -1; + } + + /* startup service classes main and late_start */ + property_set("vold.decrypt", "trigger_restart_framework"); + printf("Just triggered restart_framework\n"); + + /* Give it a few moments to get started */ + sleep(1); + } + + if (rc == 0) { + restart_successful = 1; + } + + return rc; +} + +static int do_crypto_complete(char *mount_point) +{ + struct crypt_mnt_ftr crypt_ftr; + char encrypted_state[PROPERTY_VALUE_MAX]; + char key_loc[PROPERTY_VALUE_MAX]; + + property_get("ro.crypto.state", encrypted_state, ""); + if (strcmp(encrypted_state, "encrypted") ) { + printf("not running with encryption, aborting"); + return 1; + } + + if (get_crypt_ftr_and_key(&crypt_ftr)) { + fs_mgr_get_crypt_info(fstab, key_loc, 0, sizeof(key_loc)); + + /* + * Only report this error if key_loc is a file and it exists. + * If the device was never encrypted, and /data is not mountable for + * some reason, returning 1 should prevent the UI from presenting the + * a "enter password" screen, or worse, a "press button to wipe the + * device" screen. + */ + if ((key_loc[0] == '/') && (access("key_loc", F_OK) == -1)) { + printf("master key file does not exist, aborting"); + return 1; + } else { + printf("Error getting crypt footer and key\n"); + return -1; + } + } + + if (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS) { + printf("Encryption process didn't finish successfully\n"); + return -2; /* -2 is the clue to the UI that there is no usable data on the disk, + * and give the user an option to wipe the disk */ + } + + /* We passed the test! We shall diminish, and return to the west */ + return 0; +} + +static int test_mount_encrypted_fs(char *passwd, char *mount_point, char *label) +{ + struct crypt_mnt_ftr crypt_ftr; + /* Allocate enough space for a 256 bit key, but we may use less */ + unsigned char decrypted_master_key[32]; + char crypto_blkdev[MAXPATHLEN]; + char real_blkdev[MAXPATHLEN]; + char tmp_mount_point[64]; + 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") ) { + printf("encrypted fs already validated or not running with encryption, aborting"); + return -1; + } + + fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev)); + + if (get_crypt_ftr_and_key(&crypt_ftr)) { + printf("Error getting crypt footer and key\n"); + return -1; + } + + printf("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) ) { + decrypt_master_key_and_upgrade(passwd, decrypted_master_key, &crypt_ftr); + } + + if (create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, + real_blkdev, crypto_blkdev, label)) { + printf("Error creating decrypted block device\n"); + return -1; + } + + /* If init detects an encrypted filesystem, it writes a file for each such + * encrypted fs into the tmpfs /data filesystem, and then the framework finds those + * files and passes that data to me */ + /* Create a tmp mount point to try mounting the decryptd fs + * Since we're here, the mount_point should be a tmpfs filesystem, so make + * a directory in it to test mount the decrypted filesystem. + */ + sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point); + mkdir(tmp_mount_point, 0755); + if (fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, tmp_mount_point)) { + printf("Error temp mounting decrypted block device\n"); + delete_crypto_blk_dev(label); + crypt_ftr.failed_decrypt_count++; + } else { + /* Success, so just 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 + * so we can mount it when restarting the framework. + */ + property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev); + + /* Also save a the master key so we can reencrypted the key + * the key when we want to change the password on it. + */ + memcpy(saved_master_key, decrypted_master_key, KEY_LEN_BYTES); + saved_mount_point = strdup(mount_point); + master_key_saved = 1; + rc = 0; + } + + return rc; +} + +/* Called by vold when it wants to undo the crypto mapping of a volume it + * manages. This is usually in response to a factory reset, when we want + * to undo the crypto mapping so the volume is formatted in the clear. + */ +int cryptfs_revert_volume(const char *label) +{ + return delete_crypto_blk_dev((char *)label); +} + +/* + * Called by vold when it's asked to mount an encrypted, nonremovable volume. + * Setup a dm-crypt mapping, use the saved master key from + * setting up the /data mapping, and return the new device path. + */ +int cryptfs_setup_volume(const char *label, int major, int minor, + char *crypto_sys_path, unsigned int max_path, + int *new_major, int *new_minor) +{ + char real_blkdev[MAXPATHLEN], crypto_blkdev[MAXPATHLEN]; + struct crypt_mnt_ftr sd_crypt_ftr; + struct stat statbuf; + int nr_sec, fd; + + sprintf(real_blkdev, "/dev/block/vold/%d:%d", major, minor); + + get_crypt_ftr_and_key(&sd_crypt_ftr); + + /* Update the fs_size field to be the size of the volume */ + fd = open(real_blkdev, O_RDONLY); + nr_sec = get_blkdev_size(fd); + close(fd); + if (nr_sec == 0) { + printf("Cannot get size of volume %s\n", real_blkdev); + return -1; + } + + sd_crypt_ftr.fs_size = nr_sec; + create_crypto_blk_dev(&sd_crypt_ftr, saved_master_key, real_blkdev, + crypto_blkdev, label); + + stat(crypto_blkdev, &statbuf); + *new_major = MAJOR(statbuf.st_rdev); + *new_minor = MINOR(statbuf.st_rdev); + + /* Create path to sys entry for this block device */ + snprintf(crypto_sys_path, max_path, "/devices/virtual/block/%s", strrchr(crypto_blkdev, '/')+1); + + return 0; +} + +int cryptfs_crypto_complete(void) +{ + return do_crypto_complete("/data"); +} + +#define FSTAB_PREFIX "/fstab." + +int cryptfs_check_footer(void) +{ + int rc = -1; + char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)]; + char propbuf[PROPERTY_VALUE_MAX]; + struct crypt_mnt_ftr crypt_ftr; + + property_get("ro.hardware", propbuf, ""); + snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf); + + fstab = fs_mgr_read_fstab(fstab_filename); + if (!fstab) { + printf("failed to open %s\n", fstab_filename); + return -1; + } + + rc = get_crypt_ftr_and_key(&crypt_ftr); + + return rc; +} + +int cryptfs_check_passwd(char *passwd) +{ + int rc = -1; + char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)]; + char propbuf[PROPERTY_VALUE_MAX]; + + property_get("ro.hardware", propbuf, ""); + snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf); + + fstab = fs_mgr_read_fstab(fstab_filename); + if (!fstab) { + printf("failed to open %s\n", fstab_filename); + return -1; + } + + rc = test_mount_encrypted_fs(passwd, DATA_MNT_POINT, "userdata"); + + return rc; +} + +int cryptfs_verify_passwd(char *passwd) +{ + struct crypt_mnt_ftr crypt_ftr; + /* Allocate enough space for a 256 bit key, but we may use less */ + unsigned char decrypted_master_key[32]; + char encrypted_state[PROPERTY_VALUE_MAX]; + int rc; + + property_get("ro.crypto.state", encrypted_state, ""); + if (strcmp(encrypted_state, "encrypted") ) { + printf("device not encrypted, aborting"); + return -2; + } + + if (!master_key_saved) { + printf("encrypted fs not yet mounted, aborting"); + return -1; + } + + if (!saved_mount_point) { + printf("encrypted fs failed to save mount point, aborting"); + return -1; + } + + if (get_crypt_ftr_and_key(&crypt_ftr)) { + printf("Error getting crypt footer and key\n"); + return -1; + } + + if (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) { + /* If the device has no password, then just say the password is valid */ + rc = 0; + } else { + 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; + } else { + /* If incorrect, sleep for a bit to prevent dictionary attacks */ + sleep(1); + rc = 1; + } + } + + return rc; +} + +/* Initialize a crypt_mnt_ftr structure. The keysize is + * defaulted to 16 bytes, and the filesystem size to 0. + * Presumably, at a minimum, the caller will update the + * filesystem size and crypto_type_name after calling this function. + */ +static void cryptfs_init_crypt_mnt_ftr(struct crypt_mnt_ftr *ftr) +{ + off64_t off; + + memset(ftr, 0, sizeof(struct crypt_mnt_ftr)); + ftr->magic = CRYPT_MNT_MAGIC; + 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; + ftr->persist_data_offset[1] = off + CRYPT_FOOTER_TO_PERSIST_OFFSET + + ftr->persist_data_size; + } +} + +static int cryptfs_enable_wipe(char *crypto_blkdev, off64_t size, int type) +{ + return -1; +} + +#define CRYPT_INPLACE_BUFSIZE 4096 +#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / 512) +static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_t size, + off64_t *size_already_done, off64_t tot_size) +{ + int realfd, cryptofd; + char *buf[CRYPT_INPLACE_BUFSIZE]; + int rc = -1; + off64_t numblocks, i, remainder; + off64_t one_pct, cur_pct, new_pct; + off64_t blocks_already_done, tot_numblocks; + + if ( (realfd = open(real_blkdev, O_RDONLY)) < 0) { + printf("Error opening real_blkdev %s for inplace encrypt\n", real_blkdev); + return -1; + } + + if ( (cryptofd = open(crypto_blkdev, O_WRONLY)) < 0) { + printf("Error opening crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); + close(realfd); + return -1; + } + + /* This is pretty much a simple loop of reading 4K, and writing 4K. + * The size passed in is the number of 512 byte sectors in the filesystem. + * So compute the number of whole 4K blocks we should read/write, + * and the remainder. + */ + numblocks = size / CRYPT_SECTORS_PER_BUFSIZE; + remainder = size % CRYPT_SECTORS_PER_BUFSIZE; + tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE; + blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE; + + printf("Encrypting filesystem in place..."); + + one_pct = tot_numblocks / 100; + cur_pct = 0; + /* process the majority of the filesystem in blocks */ + for (i=0; i<numblocks; i++) { + new_pct = (i + blocks_already_done) / one_pct; + if (new_pct > 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) { + printf("Error reading real_blkdev %s for inplace encrypt\n", crypto_blkdev); + goto errout; + } + if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { + printf("Error writing crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); + goto errout; + } + } + + /* Do any remaining sectors */ + for (i=0; i<remainder; i++) { + if (unix_read(realfd, buf, 512) <= 0) { + printf("Error reading rival sectors from real_blkdev %s for inplace encrypt\n", crypto_blkdev); + goto errout; + } + if (unix_write(cryptofd, buf, 512) <= 0) { + printf("Error writing final sectors to crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); + goto errout; + } + } + + *size_already_done += size; + rc = 0; + +errout: + close(realfd); + close(cryptofd); + + return rc; +} + +#define CRYPTO_ENABLE_WIPE 1 +#define CRYPTO_ENABLE_INPLACE 2 + +#define FRAMEWORK_BOOT_WAIT 60 + +static inline int should_encrypt(struct volume_info *volume) +{ + return (volume->flags & (VOL_ENCRYPTABLE | VOL_NONREMOVABLE)) == + (VOL_ENCRYPTABLE | VOL_NONREMOVABLE); +} + +int cryptfs_enable(char *howarg, char *passwd) +{ + return -1; +} + +int cryptfs_changepw(char *newpw) +{ + struct crypt_mnt_ftr crypt_ftr; + unsigned char decrypted_master_key[KEY_LEN_BYTES]; + + /* This is only allowed after we've successfully decrypted the master key */ + if (! master_key_saved) { + printf("Key not saved, aborting"); + return -1; + } + + /* get key */ + if (get_crypt_ftr_and_key(&crypt_ftr)) { + printf("Error getting crypt footer and key"); + return -1; + } + + 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); + + return 0; +} + +static int persist_get_key(char *fieldname, char *value) +{ + unsigned int i; + + if (persist_data == NULL) { + return -1; + } + for (i = 0; i < persist_data->persist_valid_entries; i++) { + if (!strncmp(persist_data->persist_entry[i].key, fieldname, PROPERTY_KEY_MAX)) { + /* We found it! */ + strlcpy(value, persist_data->persist_entry[i].val, PROPERTY_VALUE_MAX); + return 0; + } + } + + return -1; +} + +static int persist_set_key(char *fieldname, char *value, int encrypted) +{ + unsigned int i; + unsigned int num; + struct crypt_mnt_ftr crypt_ftr; + unsigned int max_persistent_entries; + unsigned int dsize; + + if (persist_data == NULL) { + return -1; + } + + /* If encrypted, use the values from the crypt_ftr, otherwise + * use the values for the current spec. + */ + if (encrypted) { + if(get_crypt_ftr_and_key(&crypt_ftr)) { + return -1; + } + dsize = crypt_ftr.persist_data_size; + } else { + dsize = CRYPT_PERSIST_DATA_SIZE; + } + max_persistent_entries = (dsize - sizeof(struct crypt_persist_data)) / + sizeof(struct crypt_persist_entry); + + num = persist_data->persist_valid_entries; + + for (i = 0; i < num; i++) { + if (!strncmp(persist_data->persist_entry[i].key, fieldname, PROPERTY_KEY_MAX)) { + /* We found an existing entry, update it! */ + memset(persist_data->persist_entry[i].val, 0, PROPERTY_VALUE_MAX); + strlcpy(persist_data->persist_entry[i].val, value, PROPERTY_VALUE_MAX); + return 0; + } + } + + /* We didn't find it, add it to the end, if there is room */ + if (persist_data->persist_valid_entries < max_persistent_entries) { + memset(&persist_data->persist_entry[num], 0, sizeof(struct crypt_persist_entry)); + strlcpy(persist_data->persist_entry[num].key, fieldname, PROPERTY_KEY_MAX); + strlcpy(persist_data->persist_entry[num].val, value, PROPERTY_VALUE_MAX); + persist_data->persist_valid_entries++; + return 0; + } + + return -1; +} + +/* Return the value of the specified field. */ +int cryptfs_getfield(char *fieldname, char *value, int len) +{ + char temp_value[PROPERTY_VALUE_MAX]; + char real_blkdev[MAXPATHLEN]; + /* 0 is success, 1 is not encrypted, + * -1 is value not set, -2 is any other error + */ + int rc = -2; + + if (persist_data == NULL) { + load_persistent_data(); + if (persist_data == NULL) { + printf("Getfield error, cannot load persistent data"); + goto out; + } + } + + if (!persist_get_key(fieldname, temp_value)) { + /* We found it, copy it to the caller's buffer and return */ + strlcpy(value, temp_value, len); + rc = 0; + } else { + /* Sadness, it's not there. Return the error */ + rc = -1; + } + +out: + return rc; +} + +/* Set the value of the specified field. */ +int cryptfs_setfield(char *fieldname, char *value) +{ + struct crypt_persist_data stored_pdata; + struct crypt_persist_data *pdata_p; + struct crypt_mnt_ftr crypt_ftr; + char encrypted_state[PROPERTY_VALUE_MAX]; + /* 0 is success, -1 is an error */ + int rc = -1; + int encrypted = 0; + + if (persist_data == NULL) { + load_persistent_data(); + if (persist_data == NULL) { + printf("Setfield error, cannot load persistent data"); + goto out; + } + } + + property_get("ro.crypto.state", encrypted_state, ""); + if (!strcmp(encrypted_state, "encrypted") ) { + encrypted = 1; + } + + if (persist_set_key(fieldname, value, encrypted)) { + goto out; + } + + /* If we are running encrypted, save the persistent data now */ + if (encrypted) { + if (save_persistent_data()) { + printf("Setfield error, cannot save persistent data"); + goto out; + } + } + + rc = 0; + +out: + return rc; +} diff --git a/crypto/jb/cryptfs.h b/crypto/jb/cryptfs.h new file mode 100644 index 000000000..d815814d3 --- /dev/null +++ b/crypto/jb/cryptfs.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2010 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. + */ + +/* This structure starts 16,384 bytes before the end of a hardware + * partition that is encrypted, or in a separate partition. It's location + * is specified by a property set in init.<device>.rc. + * The structure allocates 48 bytes for a key, but the real key size is + * specified in the struct. Currently, the code is hardcoded to use 128 + * bit keys. + * The fields after salt are only valid in rev 1.1 and later stuctures. + * Obviously, the filesystem does not include the last 16 kbytes + * of the partition if the crypt_mnt_ftr lives at the end of the + * partition. + */ + +#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 + +#define MAX_CRYPTO_TYPE_NAME_LEN 64 + +#define MAX_KEY_LEN 48 +#define SALT_LEN 16 + +/* definitions of flags in the structure below */ +#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */ +#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Set when starting encryption, + * clear when done before rebooting */ + +#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 __le8 unsigned char + +struct crypt_mnt_ftr { + __le32 magic; /* See above */ + __le16 major_version; + __le16 minor_version; + __le32 ftr_size; /* in bytes, not including key following */ + __le32 flags; /* See above */ + __le32 keysize; /* in bytes */ + __le32 spare1; /* ignored */ + __le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */ + __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and + mount, set to 0 on successful mount */ + unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption + needed to decrypt this + partition, null terminated */ + __le32 spare2; /* ignored */ + unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */ + unsigned char salt[SALT_LEN]; /* The salt used for this encryption */ + __le64 persist_data_offset[2]; /* Absolute offset to both copies of crypt_persist_data + * on device with that info, either the footer of the + * real_blkdevice or the metadata partition. */ + + __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. + * Things like airplane mode, locale and timezone are kept + * here and can be retrieved by the CryptKeeper UI to properly + * configure the phone before asking for the password + * This is only valid if the major and minor version above + * is set to 1.1 or higher. + * + * This is a 4K structure. There are 2 copies, and the code alternates + * writing one and then clearing the previous one. The reading + * code reads the first valid copy it finds, based on the magic number. + * The absolute offset to the first of the two copies is kept in rev 1.1 + * and higher crypt_mnt_ftr structures. + */ +struct crypt_persist_entry { + char key[PROPERTY_KEY_MAX]; + char val[PROPERTY_VALUE_MAX]; +}; + +/* Should be exactly 4K in size */ +struct crypt_persist_data { + __le32 persist_magic; + __le32 persist_valid_entries; + __le32 persist_spare[30]; + struct crypt_persist_entry persist_entry[0]; +}; + +struct volume_info { + unsigned int size; + unsigned int flags; + struct crypt_mnt_ftr crypt_ftr; + char mnt_point[256]; + char blk_dev[256]; + char crypto_blkdev[256]; + char label[256]; +}; +#define VOL_NONREMOVABLE 0x1 +#define VOL_ENCRYPTABLE 0x2 +#define VOL_PRIMARY 0x4 +#define VOL_PROVIDES_ASEC 0x8 + +#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_footer(void); + int cryptfs_check_passwd(char *pw); + int cryptfs_verify_passwd(char *newpw); + int cryptfs_restart(void); + int cryptfs_enable(char *flag, char *passwd); + int cryptfs_changepw(char *newpw); + int cryptfs_setup_volume(const char *label, int major, int minor, + char *crypto_dev_path, unsigned int max_pathlen, + int *new_major, int *new_minor); + int cryptfs_revert_volume(const char *label); + int cryptfs_getfield(char *fieldname, char *value, int len); + int cryptfs_setfield(char *fieldname, char *value); +#ifdef __cplusplus +} +#endif + diff --git a/crypto/libcrypt_samsung/Android.mk b/crypto/libcrypt_samsung/Android.mk new file mode 100644 index 000000000..6e0e86903 --- /dev/null +++ b/crypto/libcrypt_samsung/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH := $(call my-dir) + +ifneq ($(TARGET_SIMULATOR),true) + +include $(CLEAR_VARS) +LOCAL_MODULE := libcrypt_samsung +LOCAL_SRC_FILES := $(LOCAL_MODULE).c +LOCAL_MODULE_TAGS := eng +include $(BUILD_STATIC_LIBRARY) + +endif diff --git a/crypto/libcrypt_samsung/include/libcrypt_samsung.h b/crypto/libcrypt_samsung/include/libcrypt_samsung.h new file mode 100644 index 000000000..2fb6f2c4f --- /dev/null +++ b/crypto/libcrypt_samsung/include/libcrypt_samsung.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013 a3955269 all rights reversed, no rights reserved. + */ + +#ifndef __LIBCRYPT_SAMSUNG_H__ +#define __LIBCRYPT_SAMSUNG_H__ + +////////////////////////////////////////////////////////////////////////////// +// Name Address Ordinal +// ---- ------- ------- +// SECKM_AES_set_encrypt_key 000010D8 +// SECKM_AES_set_decrypt_key 00001464 +// SECKM_AES_encrypt 00001600 +// SECKM_AES_decrypt 00001A10 +// SECKM_aes_selftest 00001D94 +// verify_EDK 00001F7C +// encrypt_dek 00001FC8 +// decrypt_EDK 000020D4 +// change_EDK 0000218C +// generate_dek_salt 000022A4 +// create_EDK 000023A0 +// free_DEK 000024DC +// alloc_DEK 000024F4 +// SECKM_HMAC_SHA256 00002500 +// SECKM_HMAC_SHA256_selftest 00002690 +// pbkdf 000026FC +// pbkdf_selftest 00002898 +// _SECKM_PRNG_get16 00002958 +// SECKM_PRNG_get16 00002C48 +// _SECKM_PRNG_init 00002C54 +// SECKM_PRNG_selftest 00002F38 +// SECKM_PRNG_set_seed 00002FF0 +// SECKM_PRNG_init 00002FF8 +// SECKM_SHA256_Transform 00003004 +// SECKM_SHA256_Final 000031D8 +// SECKM_SHA256_Update 00003330 +// SECKM_SHA256_Init 000033FC +// SECKM_SHA2_selftest 00003430 +// integrity_check 00003488 +// update_system_property 00003580 +// setsec_km_fips_status 00003630 +// _all_checks 00003684 +// get_fips_status 000036D4 + + +// EDK Payload is defined as: +// Encrypted DEK – EDK itself +// HMAC of EDK (32 bytes ???) +// Salt 16 bytes + +#define EDK_MAGIC 0x1001e4b1 + +#pragma pack(1) + +typedef struct { + unsigned int magic; // EDK_MAGIC + unsigned int flags; // 2 + unsigned int zeros[6]; +} dek_t; + +typedef struct { + unsigned char data[32]; +} edk_t; + + +// size 0x70 -> 112 +typedef struct { + dek_t dek; + edk_t edk; + unsigned char hmac[32]; + unsigned char salt[16]; +} edk_payload_t; + +#pragma pack() + +////////////////////////////////////////////////////////////////////////////// + +int decrypt_EDK( + dek_t *dek, const edk_payload_t *edk, /*const*/ char *passwd); + +typedef int (*decrypt_EDK_t)( + dek_t *dek, const edk_payload_t *edk, /*const*/ char *passwd); + + +int verify_EDK(const edk_payload_t *edk, const char *passwd); +//change_EDK() +//create_EDK() + +// internally just mallocs 32 bytes +dek_t *alloc_DEK(); +void free_DEK(dek_t *dek); +//encrypt_dek() +//generate_dek_salt() + +//pbkdf(_buf_, "passwordPASSWORDpassword", 0x18, "saltSALTsaltSALTsaltSALTsaltSALTsalt", 0x24, 0x1000, 0x140); +int pbkdf( + void *buf, void *pw, int pwlen, void *salt, int saltlen, int hashcnt, + int keylen); + +// getprop("rw.km_fips_status") +// "ready, undefined, error_selftest, error_integrity" +int get_fips_status(); + +////////////////////////////////////////////////////////////////////////////// +// +// libsec_ecryptfs.so (internally uses libkeyutils.so) +// +// Name Address Ordinal +// ---- ------- ------- +// unmount_ecryptfs_drive 00000A78 +// mount_ecryptfs_drive 00000B48 +// fips_read_edk 00000E44 +// fips_save_edk 00000EA4 +// fips_create_edk 00000F20 +// fips_change_password 00001018 +// fips_delete_edk 00001124 +// + +// might depend on /data beeing mounted for reading /data/system/edk_p_sd +// +// filter +// 0: building options without file encryption filtering. +// 1: building options with media files filtering. +// 2: building options with all new files filtering. + +int mount_ecryptfs_drive( + const char *passwd, const char *source, const char *target, int filter); + +typedef int (*mount_ecryptfs_drive_t)( + const char *passwd, const char *source, const char *target, int filter); + +// calls 2 times umount2(source, MNT_EXPIRE) +int unmount_ecryptfs_drive( + const char *source); + +typedef int (*unmount_ecryptfs_drive_t)( + const char *source); + +////////////////////////////////////////////////////////////////////////////// + +#endif // #ifndef __LIBCRYPT_SAMSUNG_H__ + +////////////////////////////////////////////////////////////////////////////// + diff --git a/crypto/libcrypt_samsung/libcrypt_samsung.c b/crypto/libcrypt_samsung/libcrypt_samsung.c new file mode 100644 index 000000000..cd3a17804 --- /dev/null +++ b/crypto/libcrypt_samsung/libcrypt_samsung.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 a3955269 all rights reversed, no rights reserved. + */ + +////////////////////////////////////////////////////////////////////////////// + +#include <string.h> +#include <stdio.h> +#include <dlfcn.h> + +#include "include/libcrypt_samsung.h" + +////////////////////////////////////////////////////////////////////////////// + +int decrypt_EDK( + dek_t *dek, const edk_payload_t *edk, /*const*/ char *passwd) +{ + void *lib = dlopen("libsec_km.so", RTLD_LAZY); + + if(!lib) + return -100; + + int r = -101; + decrypt_EDK_t sym = (decrypt_EDK_t)dlsym(lib, "decrypt_EDK"); + if(sym) + r = sym(dek, edk, passwd); + + dlclose(lib); + + return r; +} + +int mount_ecryptfs_drive( + const char *passwd, const char *source, const char *target, int filter) +{ + void *lib = dlopen("libsec_ecryptfs.so", RTLD_LAZY); + if(!lib) + return -100; + + int r = -101; + mount_ecryptfs_drive_t sym = (mount_ecryptfs_drive_t)dlsym(lib, "mount_ecryptfs_drive"); + if(sym) + r = sym(passwd, source, target, filter); + + dlclose(lib); + + return r; +} + +int unmount_ecryptfs_drive( + const char *source) +{ + void *lib = dlopen("libsec_ecryptfs.so", RTLD_LAZY); + if(!lib) + return -100; + + int r = -101; + unmount_ecryptfs_drive_t sym = (unmount_ecryptfs_drive_t)dlsym(lib, "unmount_ecryptfs_drive"); + if(sym) + r = sym(source); + + dlclose(lib); + + return r; +}
\ No newline at end of file diff --git a/crypto/logwrapper/Android.mk b/crypto/logwrapper/Android.mk new file mode 100644 index 000000000..01b61930d --- /dev/null +++ b/crypto/logwrapper/Android.mk @@ -0,0 +1,34 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +# ======================================================== +# Static library +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := liblogwraptwrp +LOCAL_SRC_FILES := logwrap.c +LOCAL_SHARED_LIBRARIES := libcutils liblog +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +include $(BUILD_STATIC_LIBRARY) + +# ======================================================== +# Shared library +# ======================================================== +#include $(CLEAR_VARS) +#LOCAL_MODULE := liblogwrap +#LOCAL_SHARED_LIBRARIES := libcutils liblog +#LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap +#LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +#LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +#include $(BUILD_SHARED_LIBRARY) + +# ======================================================== +# Executable +# ======================================================== +#include $(CLEAR_VARS) +#LOCAL_SRC_FILES:= logwrapper.c +#LOCAL_MODULE := logwrapper +#LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils +#include $(BUILD_EXECUTABLE) diff --git a/crypto/logwrapper/NOTICE b/crypto/logwrapper/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/crypto/logwrapper/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/crypto/logwrapper/include/logwrap/logwrap.h b/crypto/logwrapper/include/logwrap/logwrap.h new file mode 100644 index 000000000..4307a3055 --- /dev/null +++ b/crypto/logwrapper/include/logwrap/logwrap.h @@ -0,0 +1,87 @@ +/* system/core/include/logwrap/logwrap.h + * + * Copyright 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. + */ + +#ifndef __LIBS_LOGWRAP_H +#define __LIBS_LOGWRAP_H + +#include <stdbool.h> + +__BEGIN_DECLS + +/* + * Run a command while logging its stdout and stderr + * + * WARNING: while this function is running it will clear all SIGCHLD handlers + * if you rely on SIGCHLD in the caller there is a chance zombies will be + * created if you're not calling waitpid after calling this. This function will + * log a warning when it clears SIGCHLD for processes other than the child it + * created. + * + * Arguments: + * argc: the number of elements in argv + * argv: an array of strings containing the command to be executed and its + * arguments as separate strings. argv does not need to be + * NULL-terminated + * status: the equivalent child status as populated by wait(status). This + * value is only valid when logwrap successfully completes. If NULL + * the return value of the child will be the function's return value. + * ignore_int_quit: set to true if you want to completely ignore SIGINT and + * SIGQUIT while logwrap is running. This may force the end-user to + * send a signal twice to signal the caller (once for the child, and + * once for the caller) + * log_target: Specify where to log the output of the child, either LOG_NONE, + * LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel + * log), or LOG_FILE (and you need to specify a pathname in the + * file_path argument, otherwise pass NULL). These are bit fields, + * and can be OR'ed together to log to multiple places. + * abbreviated: If true, capture up to the first 100 lines and last 4K of + * output from the child. The abbreviated output is not dumped to + * the specified log until the child has exited. + * file_path: if log_target has the LOG_FILE bit set, then this parameter + * must be set to the pathname of the file to log to. + * + * Return value: + * 0 when logwrap successfully run the child process and captured its status + * -1 when an internal error occurred + * -ECHILD if status is NULL and the child didn't exit properly + * the return value of the child if it exited properly and status is NULL + * + */ + +/* Values for the log_target parameter android_fork_execvp_ext() */ +#define LOG_NONE 0 +#define LOG_ALOG 1 +#define LOG_KLOG 2 +#define LOG_FILE 4 + +int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit, + int log_target, bool abbreviated, char *file_path); + +/* Similar to above, except abbreviated logging is not available, and if logwrap + * is true, logging is to the Android system log, and if false, there is no + * logging. + */ +static inline int android_fork_execvp(int argc, char* argv[], int *status, + bool ignore_int_quit, bool logwrap) +{ + return android_fork_execvp_ext(argc, argv, status, ignore_int_quit, + (logwrap ? LOG_ALOG : LOG_NONE), false, NULL); +} + +__END_DECLS + +#endif /* __LIBS_LOGWRAP_H */ diff --git a/crypto/logwrapper/logwrap.c b/crypto/logwrapper/logwrap.c new file mode 100644 index 000000000..4ca1db4c8 --- /dev/null +++ b/crypto/logwrapper/logwrap.c @@ -0,0 +1,569 @@ +/* + * Copyright (C) 2008 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 <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <poll.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdbool.h> +#include <pthread.h> + +#include <logwrap/logwrap.h> +#include "private/android_filesystem_config.h" +#include "cutils/log.h" +#include <cutils/klog.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER; + +#define ERROR(fmt, args...) \ +do { \ + fprintf(stderr, fmt, ## args); \ + ALOG(LOG_ERROR, "logwrapper", fmt, ## args); \ +} while(0) + +#define FATAL_CHILD(fmt, args...) \ +do { \ + ERROR(fmt, ## args); \ + _exit(-1); \ +} while(0) + +#define MAX_KLOG_TAG 16 + +/* This is a simple buffer that holds up to the first beginning_buf->buf_size + * bytes of output from a command. + */ +#define BEGINNING_BUF_SIZE 0x1000 +struct beginning_buf { + char *buf; + size_t alloc_len; + /* buf_size is the usable space, which is one less than the allocated size */ + size_t buf_size; + size_t used_len; +}; + +/* This is a circular buf that holds up to the last ending_buf->buf_size bytes + * of output from a command after the first beginning_buf->buf_size bytes + * (which are held in beginning_buf above). + */ +#define ENDING_BUF_SIZE 0x1000 +struct ending_buf { + char *buf; + ssize_t alloc_len; + /* buf_size is the usable space, which is one less than the allocated size */ + ssize_t buf_size; + ssize_t used_len; + /* read and write offsets into the circular buffer */ + int read; + int write; +}; + + /* A structure to hold all the abbreviated buf data */ +struct abbr_buf { + struct beginning_buf b_buf; + struct ending_buf e_buf; + int beginning_buf_full; +}; + +/* Collect all the various bits of info needed for logging in one place. */ +struct log_info { + int log_target; + char klog_fmt[MAX_KLOG_TAG * 2]; + char *btag; + bool abbreviated; + FILE *fp; + struct abbr_buf a_buf; +}; + +/* Forware declaration */ +static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen); + +/* Return 0 on success, and 1 when full */ +static int add_line_to_linear_buf(struct beginning_buf *b_buf, + char *line, ssize_t line_len) +{ + size_t new_len; + char *new_buf; + int full = 0; + + if ((line_len + b_buf->used_len) > b_buf->buf_size) { + full = 1; + } else { + /* Add to the end of the buf */ + memcpy(b_buf->buf + b_buf->used_len, line, line_len); + b_buf->used_len += line_len; + } + + return full; +} + +static void add_line_to_circular_buf(struct ending_buf *e_buf, + char *line, ssize_t line_len) +{ + ssize_t free_len; + ssize_t needed_space; + char *new_buf; + int cnt; + + if (e_buf->buf == NULL) { + return; + } + + if (line_len > e_buf->buf_size) { + return; + } + + free_len = e_buf->buf_size - e_buf->used_len; + + if (line_len > free_len) { + /* remove oldest entries at read, and move read to make + * room for the new string */ + needed_space = line_len - free_len; + e_buf->read = (e_buf->read + needed_space) % e_buf->buf_size; + e_buf->used_len -= needed_space; + } + + /* Copy the line into the circular buffer, dealing with possible + * wraparound. + */ + cnt = MIN(line_len, e_buf->buf_size - e_buf->write); + memcpy(e_buf->buf + e_buf->write, line, cnt); + if (cnt < line_len) { + memcpy(e_buf->buf, line + cnt, line_len - cnt); + } + e_buf->used_len += line_len; + e_buf->write = (e_buf->write + line_len) % e_buf->buf_size; +} + +/* Log directly to the specified log */ +static void do_log_line(struct log_info *log_info, char *line) { + if (log_info->log_target & LOG_KLOG) { + klog_write(6, log_info->klog_fmt, line); + } + if (log_info->log_target & LOG_ALOG) { + ALOG(LOG_INFO, log_info->btag, "%s", line); + } + if (log_info->log_target & LOG_FILE) { + fprintf(log_info->fp, "%s\n", line); + } +} + +/* Log to either the abbreviated buf, or directly to the specified log + * via do_log_line() above. + */ +static void log_line(struct log_info *log_info, char *line, int len) { + if (log_info->abbreviated) { + add_line_to_abbr_buf(&log_info->a_buf, line, len); + } else { + do_log_line(log_info, line); + } +} + +/* + * The kernel will take a maximum of 1024 bytes in any single write to + * the kernel logging device file, so find and print each line one at + * a time. The allocated size for buf should be at least 1 byte larger + * than buf_size (the usable size of the buffer) to make sure there is + * room to temporarily stuff a null byte to terminate a line for logging. + */ +static void print_buf_lines(struct log_info *log_info, char *buf, int buf_size) +{ + char *line_start; + char c; + int line_len; + int i; + + line_start = buf; + for (i = 0; i < buf_size; i++) { + if (*(buf + i) == '\n') { + /* Found a line ending, print the line and compute new line_start */ + /* Save the next char and replace with \0 */ + c = *(buf + i + 1); + *(buf + i + 1) = '\0'; + do_log_line(log_info, line_start); + /* Restore the saved char */ + *(buf + i + 1) = c; + line_start = buf + i + 1; + } else if (*(buf + i) == '\0') { + /* The end of the buffer, print the last bit */ + do_log_line(log_info, line_start); + break; + } + } + /* If the buffer was completely full, and didn't end with a newline, just + * ignore the partial last line. + */ +} + +static void init_abbr_buf(struct abbr_buf *a_buf) { + char *new_buf; + + memset(a_buf, 0, sizeof(struct abbr_buf)); + new_buf = malloc(BEGINNING_BUF_SIZE); + if (new_buf) { + a_buf->b_buf.buf = new_buf; + a_buf->b_buf.alloc_len = BEGINNING_BUF_SIZE; + a_buf->b_buf.buf_size = BEGINNING_BUF_SIZE - 1; + } + new_buf = malloc(ENDING_BUF_SIZE); + if (new_buf) { + a_buf->e_buf.buf = new_buf; + a_buf->e_buf.alloc_len = ENDING_BUF_SIZE; + a_buf->e_buf.buf_size = ENDING_BUF_SIZE - 1; + } +} + +static void free_abbr_buf(struct abbr_buf *a_buf) { + free(a_buf->b_buf.buf); + free(a_buf->e_buf.buf); +} + +static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen) { + if (!a_buf->beginning_buf_full) { + a_buf->beginning_buf_full = + add_line_to_linear_buf(&a_buf->b_buf, linebuf, linelen); + } + if (a_buf->beginning_buf_full) { + add_line_to_circular_buf(&a_buf->e_buf, linebuf, linelen); + } +} + +static void print_abbr_buf(struct log_info *log_info) { + struct abbr_buf *a_buf = &log_info->a_buf; + + /* Add the abbreviated output to the kernel log */ + if (a_buf->b_buf.alloc_len) { + print_buf_lines(log_info, a_buf->b_buf.buf, a_buf->b_buf.used_len); + } + + /* Print an ellipsis to indicate that the buffer has wrapped or + * is full, and some data was not logged. + */ + if (a_buf->e_buf.used_len == a_buf->e_buf.buf_size) { + do_log_line(log_info, "...\n"); + } + + if (a_buf->e_buf.used_len == 0) { + return; + } + + /* Simplest way to print the circular buffer is allocate a second buf + * of the same size, and memcpy it so it's a simple linear buffer, + * and then cal print_buf_lines on it */ + if (a_buf->e_buf.read < a_buf->e_buf.write) { + /* no wrap around, just print it */ + print_buf_lines(log_info, a_buf->e_buf.buf + a_buf->e_buf.read, + a_buf->e_buf.used_len); + } else { + /* The circular buffer will always have at least 1 byte unused, + * so by allocating alloc_len here we will have at least + * 1 byte of space available as required by print_buf_lines(). + */ + char * nbuf = malloc(a_buf->e_buf.alloc_len); + if (!nbuf) { + return; + } + int first_chunk_len = a_buf->e_buf.buf_size - a_buf->e_buf.read; + memcpy(nbuf, a_buf->e_buf.buf + a_buf->e_buf.read, first_chunk_len); + /* copy second chunk */ + memcpy(nbuf + first_chunk_len, a_buf->e_buf.buf, a_buf->e_buf.write); + print_buf_lines(log_info, nbuf, first_chunk_len + a_buf->e_buf.write); + free(nbuf); + } +} + +static int parent(const char *tag, int parent_read, pid_t pid, + int *chld_sts, int log_target, bool abbreviated, char *file_path) { + int status = 0; + char buffer[4096]; + struct pollfd poll_fds[] = { + [0] = { + .fd = parent_read, + .events = POLLIN, + }, + }; + int rc = 0; + int fd; + + struct log_info log_info; + + int a = 0; // start index of unprocessed data + int b = 0; // end index of unprocessed data + int sz; + bool found_child = false; + char tmpbuf[256]; + + log_info.btag = basename(tag); + if (!log_info.btag) { + log_info.btag = (char*) tag; + } + + if (abbreviated && (log_target == LOG_NONE)) { + abbreviated = 0; + } + if (abbreviated) { + init_abbr_buf(&log_info.a_buf); + } + + if (log_target & LOG_KLOG) { + snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt), + "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag); + } + + if ((log_target & LOG_FILE) && !file_path) { + /* No file_path specified, clear the LOG_FILE bit */ + log_target &= ~LOG_FILE; + } + + if (log_target & LOG_FILE) { + fd = open(file_path, O_WRONLY | O_CREAT, 0664); + if (fd < 0) { + ERROR("Cannot log to file %s\n", file_path); + log_target &= ~LOG_FILE; + } else { + lseek(fd, 0, SEEK_END); + log_info.fp = fdopen(fd, "a"); + } + } + + log_info.log_target = log_target; + log_info.abbreviated = abbreviated; + + while (!found_child) { + if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) { + ERROR("poll failed\n"); + rc = -1; + goto err_poll; + } + + if (poll_fds[0].revents & POLLIN) { + sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b); + + sz += b; + // Log one line at a time + for (b = 0; b < sz; b++) { + if (buffer[b] == '\r') { + if (abbreviated) { + /* The abbreviated logging code uses newline as + * the line separator. Lucikly, the pty layer + * helpfully cooks the output of the command + * being run and inserts a CR before NL. So + * I just change it to NL here when doing + * abbreviated logging. + */ + buffer[b] = '\n'; + } else { + buffer[b] = '\0'; + } + } else if (buffer[b] == '\n') { + buffer[b] = '\0'; + log_line(&log_info, &buffer[a], b - a); + a = b + 1; + } + } + + if (a == 0 && b == sizeof(buffer) - 1) { + // buffer is full, flush + buffer[b] = '\0'; + log_line(&log_info, &buffer[a], b - a); + b = 0; + } else if (a != b) { + // Keep left-overs + b -= a; + memmove(buffer, &buffer[a], b); + a = 0; + } else { + a = 0; + b = 0; + } + } + + if (poll_fds[0].revents & POLLHUP) { + int ret; + + ret = waitpid(pid, &status, WNOHANG); + if (ret < 0) { + rc = errno; + ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno)); + goto err_waitpid; + } + if (ret > 0) { + found_child = true; + } + } + } + + if (chld_sts != NULL) { + *chld_sts = status; + } else { + if (WIFEXITED(status)) + rc = WEXITSTATUS(status); + else + rc = -ECHILD; + } + + // Flush remaining data + if (a != b) { + buffer[b] = '\0'; + log_line(&log_info, &buffer[a], b - a); + } + + /* All the output has been processed, time to dump the abbreviated output */ + if (abbreviated) { + print_abbr_buf(&log_info); + } + + if (WIFEXITED(status)) { + if (WEXITSTATUS(status)) { + snprintf(tmpbuf, sizeof(tmpbuf), + "%s terminated by exit(%d)\n", log_info.btag, WEXITSTATUS(status)); + do_log_line(&log_info, tmpbuf); + } + } else { + if (WIFSIGNALED(status)) { + snprintf(tmpbuf, sizeof(tmpbuf), + "%s terminated by signal %d\n", log_info.btag, WTERMSIG(status)); + do_log_line(&log_info, tmpbuf); + } else if (WIFSTOPPED(status)) { + snprintf(tmpbuf, sizeof(tmpbuf), + "%s stopped by signal %d\n", log_info.btag, WSTOPSIG(status)); + do_log_line(&log_info, tmpbuf); + } + } + +err_waitpid: +err_poll: + if (log_target & LOG_FILE) { + fclose(log_info.fp); /* Also closes underlying fd */ + } + if (abbreviated) { + free_abbr_buf(&log_info.a_buf); + } + return rc; +} + +static void child(int argc, char* argv[]) { + // create null terminated argv_child array + char* argv_child[argc + 1]; + memcpy(argv_child, argv, argc * sizeof(char *)); + argv_child[argc] = NULL; + + if (execvp(argv_child[0], argv_child)) { + FATAL_CHILD("executing %s failed: %s\n", argv_child[0], + strerror(errno)); + } +} + +int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit, + int log_target, bool abbreviated, char *file_path) { + pid_t pid; + int parent_ptty; + int child_ptty; + char *child_devname = NULL; + struct sigaction intact; + struct sigaction quitact; + sigset_t blockset; + sigset_t oldset; + int rc = 0; + + rc = pthread_mutex_lock(&fd_mutex); + if (rc) { + ERROR("failed to lock signal_fd mutex\n"); + goto err_lock; + } + + /* Use ptty instead of socketpair so that STDOUT is not buffered */ + parent_ptty = open("/dev/ptmx", O_RDWR); + if (parent_ptty < 0) { + ERROR("Cannot create parent ptty\n"); + rc = -1; + goto err_open; + } + + if (grantpt(parent_ptty) || unlockpt(parent_ptty) || + ((child_devname = (char*)ptsname(parent_ptty)) == 0)) { + ERROR("Problem with /dev/ptmx\n"); + rc = -1; + goto err_ptty; + } + + child_ptty = open(child_devname, O_RDWR); + if (child_ptty < 0) { + ERROR("Cannot open child_ptty\n"); + rc = -1; + goto err_child_ptty; + } + + sigemptyset(&blockset); + sigaddset(&blockset, SIGINT); + sigaddset(&blockset, SIGQUIT); + pthread_sigmask(SIG_BLOCK, &blockset, &oldset); + + pid = fork(); + if (pid < 0) { + close(child_ptty); + ERROR("Failed to fork\n"); + rc = -1; + goto err_fork; + } else if (pid == 0) { + pthread_mutex_unlock(&fd_mutex); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + close(parent_ptty); + + // redirect stdout and stderr + dup2(child_ptty, 1); + dup2(child_ptty, 2); + close(child_ptty); + + child(argc, argv); + } else { + close(child_ptty); + if (ignore_int_quit) { + struct sigaction ignact; + + memset(&ignact, 0, sizeof(ignact)); + ignact.sa_handler = SIG_IGN; + sigaction(SIGINT, &ignact, &intact); + sigaction(SIGQUIT, &ignact, &quitact); + } + + rc = parent(argv[0], parent_ptty, pid, status, log_target, + abbreviated, file_path); + } + + if (ignore_int_quit) { + sigaction(SIGINT, &intact, NULL); + sigaction(SIGQUIT, &quitact, NULL); + } +err_fork: + pthread_sigmask(SIG_SETMASK, &oldset, NULL); +err_child_ptty: +err_ptty: + close(parent_ptty); +err_open: + pthread_mutex_unlock(&fd_mutex); +err_lock: + return rc; +} diff --git a/crypto/logwrapper/logwrapper.c b/crypto/logwrapper/logwrapper.c new file mode 100644 index 000000000..d0d8d1471 --- /dev/null +++ b/crypto/logwrapper/logwrapper.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2008 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 <stdio.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <logwrap/logwrap.h> +#include <cutils/klog.h> + +#include "cutils/log.h" + +void fatal(const char *msg) { + fprintf(stderr, "%s", msg); + ALOG(LOG_ERROR, "logwrapper", "%s", msg); + exit(-1); +} + +void usage() { + fatal( + "Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...]\n" + "\n" + "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n" + "the Android logging system. Tag is set to BINARY, priority is\n" + "always LOG_INFO.\n" + "\n" + "-a: Causes logwrapper to do abbreviated logging.\n" + " This logs up to the first 4K and last 4K of the command\n" + " being run, and logs the output when the command exits\n" + "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n" + " fault address is set to the status of wait()\n" + "-k: Causes logwrapper to log to the kernel log instead of\n" + " the Android system log\n"); +} + +int main(int argc, char* argv[]) { + int seg_fault_on_exit = 0; + int log_target = LOG_ALOG; + bool abbreviated = false; + int ch; + int status = 0xAAAA; + int rc; + + while ((ch = getopt(argc, argv, "adk")) != -1) { + switch (ch) { + case 'a': + abbreviated = true; + break; + case 'd': + seg_fault_on_exit = 1; + break; + case 'k': + log_target = LOG_KLOG; + klog_set_level(6); + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + } + + rc = android_fork_execvp_ext(argc, &argv[0], &status, true, + log_target, abbreviated, NULL); + if (!rc) { + if (WIFEXITED(status)) + rc = WEXITSTATUS(status); + else + rc = -ECHILD; + } + + if (seg_fault_on_exit) { + *(int *)status = 0; // causes SIGSEGV with fault_address = status + } + + return rc; +} diff --git a/crypto/scrypt/Android.mk b/crypto/scrypt/Android.mk new file mode 100644 index 000000000..7fa96d06e --- /dev/null +++ b/crypto/scrypt/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH := $(call my-dir) + +# Enable to be able to use ALOG* with #include "cutils/log.h" +#log_c_includes += system/core/include +#log_shared_libraries := liblog + +# These makefiles are here instead of being Android.mk files in the +# respective crypto, ssl, and apps directories so +# that import_openssl.sh import won't remove them. +include $(LOCAL_PATH)/build-config.mk +include $(LOCAL_PATH)/Scrypt.mk + +include $(call all-named-subdir-makefiles,tests) diff --git a/crypto/scrypt/MODULE_LICENSE_BSD_LIKE b/crypto/scrypt/MODULE_LICENSE_BSD_LIKE new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/crypto/scrypt/MODULE_LICENSE_BSD_LIKE diff --git a/crypto/scrypt/NOTICE b/crypto/scrypt/NOTICE new file mode 100644 index 000000000..b0b9311e6 --- /dev/null +++ b/crypto/scrypt/NOTICE @@ -0,0 +1,36 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +/* + * version 20110505 + * D. J. Bernstein + * Public domain. + * + * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419 + */ diff --git a/crypto/scrypt/Scrypt-config.mk b/crypto/scrypt/Scrypt-config.mk new file mode 100644 index 000000000..c0b00d873 --- /dev/null +++ b/crypto/scrypt/Scrypt-config.mk @@ -0,0 +1,105 @@ +# Auto-generated - DO NOT EDIT! +# To regenerate, edit scrypt.config, then run: +# ./import_scrypt.sh import /path/to/scrypt-1.1.6.tar.gz +# +# Before including this file, the local Android.mk must define the following +# variables: +# +# local_c_flags +# local_c_includes +# local_additional_dependencies +# +# This script will define the following variables: +# +# target_c_flags +# target_c_includes +# target_src_files +# +# host_c_flags +# host_c_includes +# host_src_files +# + +# Ensure these are empty. +unknown_arch_c_flags := +unknown_arch_src_files := +unknown_arch_exclude_files := + + +common_c_flags := + +common_src_files := \ + lib/crypto/crypto_scrypt-ref.c \ + +common_c_includes := \ + lib/crypto \ + lib/util \ + +arm_c_flags := + +arm_src_files := + +arm_exclude_files := + +arm_neon_c_flags := + +arm_neon_src_files := \ + lib/crypto/crypto_scrypt-neon.c \ + +arm_neon_exclude_files := \ + lib/crypto/crypto_scrypt-ref.c \ + +x86_c_flags := + +x86_src_files := \ + lib/crypto/crypto_scrypt-sse.c \ + +x86_exclude_files := \ + lib/crypto/crypto_scrypt-ref.c \ + +x86_64_c_flags := + +x86_64_src_files := \ + lib/crypto/crypto_scrypt-sse.c \ + +x86_64_exclude_files := \ + lib/crypto/crypto_scrypt-ref.c \ + +mips_c_flags := + +mips_src_files := + +mips_exclude_files := + +target_arch := $(TARGET_ARCH) +ifeq ($(target_arch)-$(TARGET_HAS_BIGENDIAN),mips-true) +target_arch := unknown_arch +endif + +target_c_flags := $(common_c_flags) $($(target_arch)_c_flags) $(local_c_flags) +target_c_includes := $(addprefix $(commands_recovery_local_path)/crypto/scrypt/,$(common_c_includes)) $(local_c_includes) +target_src_files := $(common_src_files) $($(target_arch)_src_files) +target_src_files := $(filter-out $($(target_arch)_exclude_files), $(target_src_files)) + +# Hacks for ARM NEON support +ifeq ($(target_arch),arm) +ifeq ($(ARCH_ARM_HAVE_NEON),true) +target_c_flags += $(arm_neon_c_flags) +target_src_files += $(arm_neon_src_files) +target_src_files := $(filter-out $(arm_neon_exclude_files), $(target_src_files)) +endif +endif + +ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86) +host_arch := x86 +else +host_arch := unknown_arch +endif + +host_c_flags := $(common_c_flags) $($(host_arch)_c_flags) $(local_c_flags) +host_c_includes := $(addprefix $(commands_recovery_local_path)/crypto/scrypt/,$(common_c_includes)) $(local_c_includes) +host_src_files := $(common_src_files) $($(host_arch)_src_files) +host_src_files := $(filter-out $($(host_arch)_exclude_files), $(host_src_files)) + +local_additional_dependencies += $(LOCAL_PATH)/Scrypt-config.mk + diff --git a/crypto/scrypt/Scrypt.mk b/crypto/scrypt/Scrypt.mk new file mode 100644 index 000000000..45af97fc2 --- /dev/null +++ b/crypto/scrypt/Scrypt.mk @@ -0,0 +1,46 @@ +local_c_flags := -DUSE_OPENSSL_PBKDF2 + +local_c_includes := $(log_c_includes) external/openssl/include + +local_additional_dependencies := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/Scrypt.mk + +include $(LOCAL_PATH)/Scrypt-config.mk + +####################################### +# target static library +include $(CLEAR_VARS) +include $(LOCAL_PATH)/android-config.mk + +LOCAL_SHARED_LIBRARIES := $(log_shared_libraries) + +# If we're building an unbundled build, don't try to use clang since it's not +# in the NDK yet. This can be removed when a clang version that is fast enough +# in the NDK. +ifeq (,$(TARGET_BUILD_APPS)) +LOCAL_CLANG := true +else +LOCAL_SDK_VERSION := 9 +endif + +LOCAL_SRC_FILES += $(target_src_files) +LOCAL_CFLAGS += $(target_c_flags) +LOCAL_C_INCLUDES += $(target_c_includes) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE:= libscrypttwrp_static +LOCAL_ADDITIONAL_DEPENDENCIES := $(local_additional_dependencies) +include $(BUILD_STATIC_LIBRARY) + +######################################## +# host static library + +include $(CLEAR_VARS) +include $(LOCAL_PATH)/android-config.mk +LOCAL_SHARED_LIBRARIES := $(log_shared_libraries) +LOCAL_SRC_FILES += $(host_src_files) +LOCAL_CFLAGS += $(host_c_flags) +LOCAL_C_INCLUDES += $(host_c_includes) +LOCAL_LDLIBS += -ldl +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE:= libscrypttwrp_static +LOCAL_ADDITIONAL_DEPENDENCIES := $(local_additional_dependencies) +include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/crypto/scrypt/android-config.mk b/crypto/scrypt/android-config.mk new file mode 100644 index 000000000..326e1134e --- /dev/null +++ b/crypto/scrypt/android-config.mk @@ -0,0 +1,16 @@ +# +# These flags represent the build-time configuration of scrypt for Android +# +# The value of $(scrypt_cflags) was pruned from the Makefile generated +# by running ./configure from import_scrypt.sh. +# +# This script performs minor but required patching for the Android build. +# + +LOCAL_CFLAGS += $(scrypt_cflags) + +# Add in flags to let config.h be read properly +LOCAL_CFLAGS += "-DHAVE_CONFIG_H" + +# Add clang here when it works on host +# LOCAL_CLANG := true diff --git a/crypto/scrypt/build-config.mk b/crypto/scrypt/build-config.mk new file mode 100644 index 000000000..3d2ab9195 --- /dev/null +++ b/crypto/scrypt/build-config.mk @@ -0,0 +1,6 @@ +# Auto-generated - DO NOT EDIT! +# To regenerate, edit scrypt.config, then run: +# ./import_scrypt.sh import /path/to/scrypt-1.1.6.tar.gz +# +scrypt_cflags := \ + diff --git a/crypto/scrypt/config.h b/crypto/scrypt/config.h new file mode 100644 index 000000000..3514f398e --- /dev/null +++ b/crypto/scrypt/config.h @@ -0,0 +1,99 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `clock_gettime' function. */ +#define HAVE_CLOCK_GETTIME 1 + +/* Define to 1 if you have the declaration of `be64enc', and to 0 if you + don't. */ +#define HAVE_DECL_BE64ENC 0 + +/* Define to 1 if you have the <err.h> header file. */ +#define HAVE_ERR_H 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `rt' library (-lrt). */ +#define HAVE_LIBRT 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `posix_memalign' function. */ +#define HAVE_POSIX_MEMALIGN 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if the system has the type `struct sysinfo'. */ +#define HAVE_STRUCT_SYSINFO 1 + +/* Define to 1 if `mem_unit' is member of `struct sysinfo'. */ +#define HAVE_STRUCT_SYSINFO_MEM_UNIT 1 + +/* Define to 1 if `totalram' is member of `struct sysinfo'. */ +#define HAVE_STRUCT_SYSINFO_TOTALRAM 1 + +/* Define to 1 if the OS has a hw.usermem sysctl */ +/* #undef HAVE_SYSCTL_HW_USERMEM */ + +/* Define to 1 if you have the `sysinfo' function. */ +#define HAVE_SYSINFO 1 + +/* Define to 1 if you have the <sys/endian.h> header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define to 1 if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/sysinfo.h> header file. */ +#define HAVE_SYS_SYSINFO_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "scrypt" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "scrypt" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "scrypt 1.1.6" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "scrypt" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.1.6" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.1.6" + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ diff --git a/crypto/scrypt/import_scrypt.sh b/crypto/scrypt/import_scrypt.sh new file mode 100755 index 000000000..324eae64b --- /dev/null +++ b/crypto/scrypt/import_scrypt.sh @@ -0,0 +1,493 @@ +#!/bin/bash +# +# Copyright (C) 2009 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. +# + +# +# This script imports new versions of scrypt (http://www.tarsnap.com/scrypt/) into the +# Android source tree. To run, (1) fetch the appropriate tarball from the scrypt repository, +# (2) check the gpg/pgp signature, and then (3) run: +# ./import_scrypt.sh import scrypt-*.tar.gz +# +# IMPORTANT: See README.android for additional details. + +# turn on exit on error as well as a warning when it happens +set -e +set -x +trap "echo WARNING: Exiting on non-zero subprocess exit code" ERR; + +# Ensure consistent sorting order / tool output. +export LANG=C +export LC_ALL=C + +export DIRNAME=$(dirname $0) + +function die() { + declare -r message=$1 + + echo $message + exit 1 +} + +function usage() { + declare -r message=$1 + + if [ ! "$message" = "" ]; then + echo $message + fi + echo "Usage:" + echo " ./import_scrypt.sh import </path/to/scrypt-*.tar.gz>" + echo " ./import_scrypt.sh regenerate <patch/*.patch>" + echo " ./import_scrypt.sh generate <patch/*.patch> </path/to/scrypt-*.tar.gz>" + exit 1 +} + +function main() { + if [ ! -d patches ]; then + die "scrypt patch directory patches/ not found" + fi + + if [ ! -f scrypt.version ]; then + die "scrypt.version not found" + fi + + source $DIRNAME/scrypt.version + if [ "$SCRYPT_VERSION" == "" ]; then + die "Invalid scrypt.version; see README.android for more information" + fi + + SCRYPT_DIR=scrypt-$SCRYPT_VERSION + SCRYPT_DIR_ORIG=$SCRYPT_DIR.orig + + if [ ! -f scrypt.config ]; then + die "scrypt.config not found" + fi + + source $DIRNAME/scrypt.config + if [ "$CONFIGURE_ARGS" == "" -o "$UNNEEDED_SOURCES" == "" -o "$NEEDED_SOURCES" == "" ]; then + die "Invalid scrypt.config; see README.android for more information" + fi + + declare -r command=$1 + shift || usage "No command specified. Try import, regenerate, or generate." + if [ "$command" = "import" ]; then + declare -r tar=$1 + shift || usage "No tar file specified." + import $tar + elif [ "$command" = "regenerate" ]; then + declare -r patch=$1 + shift || usage "No patch file specified." + [ -d $SCRYPT_DIR ] || usage "$SCRYPT_DIR not found, did you mean to use generate?" + [ -d $SCRYPT_DIR_ORIG_ORIG ] || usage "$SCRYPT_DIR_ORIG not found, did you mean to use generate?" + regenerate $patch + elif [ "$command" = "generate" ]; then + declare -r patch=$1 + shift || usage "No patch file specified." + declare -r tar=$1 + shift || usage "No tar file specified." + generate $patch $tar + else + usage "Unknown command specified $command. Try import, regenerate, or generate." + fi +} + +# Compute the name of an assembly source file generated by one of the +# gen_asm_xxxx() functions below. The logic is the following: +# - if "$2" is not empty, output it directly +# - otherwise, change the file extension of $1 from .pl to .S and output +# it. +# Usage: default_asm_file "$1" "$2" +# or default_asm_file "$@" +# +# $1: generator path (perl script) +# $2: optional output file name. +function default_asm_file () { + if [ "$2" ]; then + echo "$2" + else + echo "${1%%.pl}.S" + fi +} + +# Generate an ARM assembly file. +# $1: generator (perl script) +# $2: [optional] output file name +function gen_asm_arm () { + local OUT + OUT=$(default_asm_file "$@") + perl "$1" > "$OUT" +} + +function gen_asm_mips () { + local OUT + OUT=$(default_asm_file "$@") + # The perl scripts expect to run the target compiler as $CC to determine + # the endianess of the target. Setting CC to true is a hack that forces the scripts + # to generate little endian output + CC=true perl "$1" o32 > "$OUT" +} + +function gen_asm_x86 () { + local OUT + OUT=$(default_asm_file "$@") + perl "$1" elf -fPIC > "$OUT" +} + +function gen_asm_x86_64 () { + local OUT + OUT=$(default_asm_file "$@") + perl "$1" elf "$OUT" > "$OUT" +} + + +# Filter all items in a list that match a given pattern. +# $1: space-separated list +# $2: egrep pattern. +# Out: items in $1 that match $2 +function filter_by_egrep() { + declare -r pattern=$1 + shift + echo "$@" | tr ' ' '\n' | grep -e "$pattern" | tr '\n' ' ' +} + +# Sort and remove duplicates in a space-separated list +# $1: space-separated list +# Out: new space-separated list +function uniq_sort () { + echo "$@" | tr ' ' '\n' | sort -u | tr '\n' ' ' +} + +function print_autogenerated_header() { + echo "# Auto-generated - DO NOT EDIT!" + echo "# To regenerate, edit scrypt.config, then run:" + echo "# ./import_scrypt.sh import /path/to/scrypt-$SCRYPT_VERSION.tar.gz" + echo "#" +} + +function generate_build_config_mk() { + ./configure $CONFIGURE_ARGS + #rm -f apps/CA.pl.bak crypto/scryptconf.h.bak + + declare -r tmpfile=$(mktemp) + (grep -e -D Makefile | grep -v CONFIGURE_ARGS= | grep -v OPTIONS=) > $tmpfile + + declare -r cflags=$(filter_by_egrep "^-D" $(grep -e "^CFLAG=" $tmpfile)) + declare -r depflags=$(filter_by_egrep "^-D" $(grep -e "^DEPFLAG=" $tmpfile)) + rm -f $tmpfile + + echo "Generating $(basename $1)" + ( + print_autogenerated_header + + echo "scrypt_cflags := \\" + for cflag in $cflags $depflags; do + echo " $cflag \\" + done + echo "" + ) > $1 +} + +# Return the value of a computed variable name. +# E.g.: +# FOO=foo +# BAR=bar +# echo $(var_value FOO_$BAR) -> prints the value of ${FOO_bar} +# $1: Variable name +# Out: variable value +var_value() { + # Note: don't use 'echo' here, because it's sensitive to values + # that begin with an underscore (e.g. "-n") + eval printf \"%s\\n\" \$$1 +} + +# Same as var_value, but returns sorted output without duplicates. +# $1: Variable name +# Out: variable value (if space-separated list, sorted with no duplicates) +var_sorted_value() { + uniq_sort $(var_value $1) +} + +# Print the definition of a given variable in a GNU Make build file. +# $1: Variable name (e.g. common_src_files) +# $2+: Variable value (e.g. list of sources) +print_vardef_in_mk() { + declare -r varname=$1 + shift + if [ -z "$1" ]; then + echo "$varname :=" + else + echo "$varname := \\" + for src; do + echo " $src \\" + done + fi + echo "" +} + +# Same as print_vardef_in_mk, but print a CFLAGS definition from +# a list of compiler defines. +# $1: Variable name (e.g. common_c_flags) +# $2: List of defines (e.g. SCRYPT_NO_DONKEYS ...) +print_defines_in_mk() { + declare -r varname=$1 + shift + if [ -z "$1" ]; then + echo "$varname :=" + else + echo "$varname := \\" + for def; do + echo " -D$def \\" + done + fi + echo "" +} + +# Generate a configuration file like Scrypt-config.mk +# This uses variable definitions from scrypt.config to build a config +# file that can compute the list of target- and host-specific sources / +# compiler flags for a given component. +# +# $1: Target file name. (e.g. Scrypt-config.mk) +function generate_config_mk() { + declare -r output="$1" + declare -r all_archs="arm arm_neon x86 x86_64 mips" + + echo "Generating $(basename $output)" + ( + print_autogenerated_header + echo \ +"# Before including this file, the local Android.mk must define the following +# variables: +# +# local_c_flags +# local_c_includes +# local_additional_dependencies +# +# This script will define the following variables: +# +# target_c_flags +# target_c_includes +# target_src_files +# +# host_c_flags +# host_c_includes +# host_src_files +# + +# Ensure these are empty. +unknown_arch_c_flags := +unknown_arch_src_files := +unknown_arch_exclude_files := + +" + common_defines=$(var_sorted_value SCRYPT_DEFINES) + print_defines_in_mk common_c_flags $common_defines + + common_sources=$(var_sorted_value SCRYPT_SOURCES) + print_vardef_in_mk common_src_files $common_sources + + common_includes=$(var_sorted_value SCRYPT_INCLUDES) + print_vardef_in_mk common_c_includes $common_includes + + for arch in $all_archs; do + arch_defines=$(var_sorted_value SCRYPT_DEFINES_${arch}) + print_defines_in_mk ${arch}_c_flags $arch_defines + + arch_sources=$(var_sorted_value SCRYPT_SOURCES_${arch}) + print_vardef_in_mk ${arch}_src_files $arch_sources + + arch_exclude_sources=$(var_sorted_value SCRYPT_SOURCES_EXCLUDES_${arch}) + print_vardef_in_mk ${arch}_exclude_files $arch_exclude_sources + + done + + echo "\ +target_arch := \$(TARGET_ARCH) +ifeq (\$(target_arch)-\$(TARGET_HAS_BIGENDIAN),mips-true) +target_arch := unknown_arch +endif + +target_c_flags := \$(common_c_flags) \$(\$(target_arch)_c_flags) \$(local_c_flags) +target_c_includes := \$(addprefix external/scrypt/,\$(common_c_includes)) \$(local_c_includes) +target_src_files := \$(common_src_files) \$(\$(target_arch)_src_files) +target_src_files := \$(filter-out \$(\$(target_arch)_exclude_files), \$(target_src_files)) + +# Hacks for ARM NEON support +ifeq (\$(target_arch),arm) +ifeq (\$(ARCH_ARM_HAVE_NEON),true) +target_c_flags += \$(arm_neon_c_flags) +target_src_files += \$(arm_neon_src_files) +target_src_files := \$(filter-out \$(arm_neon_exclude_files), \$(target_src_files)) +endif +endif + +ifeq (\$(HOST_OS)-\$(HOST_ARCH),linux-x86) +host_arch := x86 +else +host_arch := unknown_arch +endif + +host_c_flags := \$(common_c_flags) \$(\$(host_arch)_c_flags) \$(local_c_flags) +host_c_includes := \$(addprefix external/scrypt/,\$(common_c_includes)) \$(local_c_includes) +host_src_files := \$(common_src_files) \$(\$(host_arch)_src_files) +host_src_files := \$(filter-out \$(\$(host_arch)_exclude_files), \$(host_src_files)) + +local_additional_dependencies += \$(LOCAL_PATH)/$(basename $output) +" + + ) > "$output" +} + +function import() { + declare -r SCRYPT_SOURCE=$1 + + untar $SCRYPT_SOURCE readonly + applypatches $SCRYPT_DIR + + cd $SCRYPT_DIR + + generate_build_config_mk ../build-config.mk + + touch ../MODULE_LICENSE_BSD_LIKE + + cd .. + + generate_config_mk Scrypt-config.mk + + # Prune unnecessary sources + prune + + NEEDED_SOURCES="$NEEDED_SOURCES" + for i in $NEEDED_SOURCES; do + echo "Updating $i" + rm -r $i + mv $SCRYPT_DIR/$i . + done + + cleantar +} + +function regenerate() { + declare -r patch=$1 + + generatepatch $patch +} + +function generate() { + declare -r patch=$1 + declare -r SCRYPT_SOURCE=$2 + + untar $SCRYPT_SOURCE + applypatches $SCRYPT_DIR_ORIG $patch + prune + + for i in $NEEDED_SOURCES; do + echo "Restoring $i" + rm -r $SCRYPT_DIR/$i + cp -rf $i $SCRYPT_DIR/$i + done + + generatepatch $patch + cleantar +} + +# Find all files in a sub-directory that are encoded in ISO-8859 +# $1: Directory. +# Out: list of files in $1 that are encoded as ISO-8859. +function find_iso8859_files() { + find $1 -type f -print0 | xargs -0 file | fgrep "ISO-8859" | cut -d: -f1 +} + +# Convert all ISO-8859 files in a given subdirectory to UTF-8 +# $1: Directory name +function convert_iso8859_to_utf8() { + declare -r iso_files=$(find_iso8859_files "$1") + for iso_file in $iso_files; do + iconv --from-code iso-8859-1 --to-code utf-8 $iso_file > $iso_file.tmp + rm -f $iso_file + mv $iso_file.tmp $iso_file + done +} + +function untar() { + declare -r SCRYPT_SOURCE=$1 + declare -r readonly=$2 + + # Remove old source + cleantar + + # Process new source + tar -zxf $SCRYPT_SOURCE + convert_iso8859_to_utf8 $SCRYPT_DIR + cp -rfP $SCRYPT_DIR $SCRYPT_DIR_ORIG + if [ ! -z $readonly ]; then + find $SCRYPT_DIR_ORIG -type f -print0 | xargs -0 chmod a-w + fi +} + +function prune() { + echo "Removing $UNNEEDED_SOURCES" + (cd $SCRYPT_DIR_ORIG && rm -rf $UNNEEDED_SOURCES) + (cd $SCRYPT_DIR && rm -r $UNNEEDED_SOURCES) +} + +function cleantar() { + rm -rf $SCRYPT_DIR_ORIG + rm -rf $SCRYPT_DIR +} + +function applypatches () { + declare -r dir=$1 + declare -r skip_patch=$2 + + cd $dir + + # Apply appropriate patches + for i in $SCRYPT_PATCHES; do + if [ ! "$skip_patch" = "patches/$i" ]; then + echo "Applying patch $i" + patch -p1 --merge < ../patches/$i || die "Could not apply patches/$i. Fix source and run: $0 regenerate patches/$i" + else + echo "Skiping patch $i" + fi + + done + + # Cleanup patch output + find . \( -type f -o -type l \) -name "*.orig" -print0 | xargs -0 rm -f + + cd .. +} + +function generatepatch() { + declare -r patch=$1 + + # Cleanup stray files before generating patch + find $SCRYPT_DIR -type f -name "*.orig" -print0 | xargs -0 rm -f + find $SCRYPT_DIR -type f -name "*~" -print0 | xargs -0 rm -f + + declare -r variable_name=SCRYPT_PATCHES_`basename $patch .patch | sed s/-/_/`_SOURCES + # http://tldp.org/LDP/abs/html/ivr.html + eval declare -r sources=\$$variable_name + rm -f $patch + touch $patch + for i in $sources; do + LC_ALL=C TZ=UTC0 diff -aup $SCRYPT_DIR_ORIG/$i $SCRYPT_DIR/$i >> $patch && die "ERROR: No diff for patch $path in file $i" + done + echo "Generated patch $patch" + echo "NOTE To make sure there are not unwanted changes from conflicting patches, be sure to review the generated patch." +} + +main $@ diff --git a/crypto/scrypt/lib/README b/crypto/scrypt/lib/README new file mode 100644 index 000000000..3bb211e84 --- /dev/null +++ b/crypto/scrypt/lib/README @@ -0,0 +1,6 @@ +The source code under this directory is taken from the client for the +Tarsnap online backup system (and released under the 2-clause BSD license +with permission of the author); keeping this code in sync with the Tarsnap +code is highly desirable and explains why there is some functionality +included here which is not actually used by the scrypt file encryption +utility. diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h b/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h new file mode 100644 index 000000000..a3b1019a7 --- /dev/null +++ b/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h @@ -0,0 +1,120 @@ +/* + * version 20110505 + * D. J. Bernstein + * Public domain. + * + * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419 + */ + +#define ROUNDS 8 +static void +salsa20_8_intrinsic(void * input) +{ + int i; + + const uint32x4_t abab = {-1,0,-1,0}; + + /* + * This is modified since we only have one argument. Usually you'd rearrange + * the constant, key, and input bytes, but we just have one linear array to + * rearrange which is a bit easier. + */ + + /* + * Change the input to be diagonals as if it's a 4x4 matrix of 32-bit values. + */ + uint32x4_t x0x5x10x15; + uint32x4_t x12x1x6x11; + uint32x4_t x8x13x2x7; + uint32x4_t x4x9x14x3; + + uint32x4_t x0x1x10x11; + uint32x4_t x12x13x6x7; + uint32x4_t x8x9x2x3; + uint32x4_t x4x5x14x15; + + uint32x4_t x0x1x2x3; + uint32x4_t x4x5x6x7; + uint32x4_t x8x9x10x11; + uint32x4_t x12x13x14x15; + + x0x1x2x3 = vld1q_u8((uint8_t *) input); + x4x5x6x7 = vld1q_u8(16 + (uint8_t *) input); + x8x9x10x11 = vld1q_u8(32 + (uint8_t *) input); + x12x13x14x15 = vld1q_u8(48 + (uint8_t *) input); + + x0x1x10x11 = vcombine_u32(vget_low_u32(x0x1x2x3), vget_high_u32(x8x9x10x11)); + x4x5x14x15 = vcombine_u32(vget_low_u32(x4x5x6x7), vget_high_u32(x12x13x14x15)); + x8x9x2x3 = vcombine_u32(vget_low_u32(x8x9x10x11), vget_high_u32(x0x1x2x3)); + x12x13x6x7 = vcombine_u32(vget_low_u32(x12x13x14x15), vget_high_u32(x4x5x6x7)); + + x0x5x10x15 = vbslq_u32(abab,x0x1x10x11,x4x5x14x15); + x8x13x2x7 = vbslq_u32(abab,x8x9x2x3,x12x13x6x7); + x4x9x14x3 = vbslq_u32(abab,x4x5x14x15,x8x9x2x3); + x12x1x6x11 = vbslq_u32(abab,x12x13x6x7,x0x1x10x11); + + uint32x4_t start0 = x0x5x10x15; + uint32x4_t start1 = x12x1x6x11; + uint32x4_t start3 = x4x9x14x3; + uint32x4_t start2 = x8x13x2x7; + + /* From here on this should be the same as the SUPERCOP version. */ + + uint32x4_t diag0 = start0; + uint32x4_t diag1 = start1; + uint32x4_t diag2 = start2; + uint32x4_t diag3 = start3; + + uint32x4_t a0; + uint32x4_t a1; + uint32x4_t a2; + uint32x4_t a3; + + for (i = ROUNDS;i > 0;i -= 2) { + a0 = diag1 + diag0; + diag3 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25); + a1 = diag0 + diag3; + diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23); + a2 = diag3 + diag2; + diag1 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19); + a3 = diag2 + diag1; + diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14); + + diag3 = vextq_u32(diag3,diag3,3); + diag2 = vextq_u32(diag2,diag2,2); + diag1 = vextq_u32(diag1,diag1,1); + + a0 = diag3 + diag0; + diag1 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25); + a1 = diag0 + diag1; + diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23); + a2 = diag1 + diag2; + diag3 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19); + a3 = diag2 + diag3; + diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14); + + diag1 = vextq_u32(diag1,diag1,3); + diag2 = vextq_u32(diag2,diag2,2); + diag3 = vextq_u32(diag3,diag3,1); + } + + x0x5x10x15 = diag0 + start0; + x12x1x6x11 = diag1 + start1; + x8x13x2x7 = diag2 + start2; + x4x9x14x3 = diag3 + start3; + + x0x1x10x11 = vbslq_u32(abab,x0x5x10x15,x12x1x6x11); + x12x13x6x7 = vbslq_u32(abab,x12x1x6x11,x8x13x2x7); + x8x9x2x3 = vbslq_u32(abab,x8x13x2x7,x4x9x14x3); + x4x5x14x15 = vbslq_u32(abab,x4x9x14x3,x0x5x10x15); + + x0x1x2x3 = vcombine_u32(vget_low_u32(x0x1x10x11),vget_high_u32(x8x9x2x3)); + x4x5x6x7 = vcombine_u32(vget_low_u32(x4x5x14x15),vget_high_u32(x12x13x6x7)); + x8x9x10x11 = vcombine_u32(vget_low_u32(x8x9x2x3),vget_high_u32(x0x1x10x11)); + x12x13x14x15 = vcombine_u32(vget_low_u32(x12x13x6x7),vget_high_u32(x4x5x14x15)); + + vst1q_u8((uint8_t *) input,(uint8x16_t) x0x1x2x3); + vst1q_u8(16 + (uint8_t *) input,(uint8x16_t) x4x5x6x7); + vst1q_u8(32 + (uint8_t *) input,(uint8x16_t) x8x9x10x11); + vst1q_u8(48 + (uint8_t *) input,(uint8x16_t) x12x13x14x15); +} diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c b/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c new file mode 100644 index 000000000..a3bf052b4 --- /dev/null +++ b/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c @@ -0,0 +1,305 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#include "scrypt_platform.h" + +#include <machine/cpu-features.h> +#include <arm_neon.h> + +#include <errno.h> +#include <stdint.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#ifdef USE_OPENSSL_PBKDF2 +#include <openssl/evp.h> +#else +#include "sha256.h" +#endif +#include "sysendian.h" + +#include "crypto_scrypt.h" + +#include "crypto_scrypt-neon-salsa208.h" + +static void blkcpy(void *, void *, size_t); +static void blkxor(void *, void *, size_t); +void crypto_core_salsa208_armneon2(void *); +static void blockmix_salsa8(uint8x16_t *, uint8x16_t *, uint8x16_t *, size_t); +static uint64_t integerify(void *, size_t); +static void smix(uint8_t *, size_t, uint64_t, void *, void *); + +static void +blkcpy(void * dest, void * src, size_t len) +{ + uint8x16_t * D = dest; + uint8x16_t * S = src; + size_t L = len / 16; + size_t i; + + for (i = 0; i < L; i++) + D[i] = S[i]; +} + +static void +blkxor(void * dest, void * src, size_t len) +{ + uint8x16_t * D = dest; + uint8x16_t * S = src; + size_t L = len / 16; + size_t i; + + for (i = 0; i < L; i++) + D[i] = veorq_u8(D[i], S[i]); +} + +/** + * blockmix_salsa8(B, Y, r): + * Compute B = BlockMix_{salsa20/8, r}(B). The input B must be 128r bytes in + * length; the temporary space Y must also be the same size. + */ +static void +blockmix_salsa8(uint8x16_t * Bin, uint8x16_t * Bout, uint8x16_t * X, size_t r) +{ + size_t i; + + /* 1: X <-- B_{2r - 1} */ + blkcpy(X, &Bin[8 * r - 4], 64); + + /* 2: for i = 0 to 2r - 1 do */ + for (i = 0; i < r; i++) { + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 8], 64); + salsa20_8_intrinsic((void *) X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 4], X, 64); + + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 8 + 4], 64); + salsa20_8_intrinsic((void *) X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[(r + i) * 4], X, 64); + } +} + +/** + * integerify(B, r): + * Return the result of parsing B_{2r-1} as a little-endian integer. + */ +static uint64_t +integerify(void * B, size_t r) +{ + uint8_t * X = (void*)((uintptr_t)(B) + (2 * r - 1) * 64); + + return (le64dec(X)); +} + +/** + * smix(B, r, N, V, XY): + * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; the + * temporary storage V must be 128rN bytes in length; the temporary storage + * XY must be 256r bytes in length. The value N must be a power of 2. + */ +static void +smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY) +{ + uint8x16_t * X = XY; + uint8x16_t * Y = (void *)((uintptr_t)(XY) + 128 * r); + uint8x16_t * Z = (void *)((uintptr_t)(XY) + 256 * r); + uint32_t * X32 = (void *)X; + uint64_t i, j; + size_t k; + + /* 1: X <-- B */ + blkcpy(X, B, 128 * r); + + /* 2: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 3: V_i <-- X */ + blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(X, Y, Z, r); + + /* 3: V_i <-- X */ + blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r), + Y, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(Y, X, Z, r); + } + + /* 6: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 7: j <-- Integerify(X) mod N */ + j = integerify(X, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); + blockmix_salsa8(X, Y, Z, r); + + /* 7: j <-- Integerify(X) mod N */ + j = integerify(Y, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); + blockmix_salsa8(Y, X, Z, r); + } + + /* 10: B' <-- X */ + blkcpy(B, X, 128 * r); +} + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2. + * + * Return 0 on success; or -1 on error. + */ +int +crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, + uint8_t * buf, size_t buflen) +{ + void * B0, * V0, * XY0; + uint8_t * B; + uint32_t * V; + uint32_t * XY; + uint32_t i; + + /* Sanity-check parameters. */ +#if SIZE_MAX > UINT32_MAX + if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { + errno = EFBIG; + goto err0; + } +#endif + if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { + errno = EFBIG; + goto err0; + } + if (((N & (N - 1)) != 0) || (N == 0)) { + errno = EINVAL; + goto err0; + } + if ((r > SIZE_MAX / 128 / p) || +#if SIZE_MAX / 256 <= UINT32_MAX + (r > SIZE_MAX / 256) || +#endif + (N > SIZE_MAX / 128 / r)) { + errno = ENOMEM; + goto err0; + } + + /* Allocate memory. */ +#ifdef HAVE_POSIX_MEMALIGN + if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) + goto err0; + B = (uint8_t *)(B0); + if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) + goto err1; + XY = (uint32_t *)(XY0); +#ifndef MAP_ANON + if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) + goto err2; + V = (uint32_t *)(V0); +#endif +#else + if ((B0 = malloc(128 * r * p + 63)) == NULL) + goto err0; + B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); + if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) + goto err1; + XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); +#ifndef MAP_ANON + if ((V0 = malloc(128 * r * N + 63)) == NULL) + goto err2; + V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); +#endif +#endif +#ifdef MAP_ANON + if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, +#ifdef MAP_NOCORE + MAP_ANON | MAP_PRIVATE | MAP_NOCORE, +#else + MAP_ANON | MAP_PRIVATE, +#endif + -1, 0)) == MAP_FAILED) + goto err2; + V = (uint32_t *)(V0); +#endif + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ +#ifdef USE_OPENSSL_PBKDF2 + PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B); +#else + PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); +#endif + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { + /* 3: B_i <-- MF(B_i, N) */ + smix(&B[i * 128 * r], r, N, V, XY); + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ +#ifdef USE_OPENSSL_PBKDF2 + PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf); +#else + PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); +#endif + + /* Free memory. */ +#ifdef MAP_ANON + if (munmap(V0, 128 * r * N)) + goto err2; +#else + free(V0); +#endif + free(XY0); + free(B0); + + /* Success! */ + return (0); + +err2: + free(XY0); +err1: + free(B0); +err0: + /* Failure! */ + return (-1); +} diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c b/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c new file mode 100644 index 000000000..abe23eaa5 --- /dev/null +++ b/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c @@ -0,0 +1,296 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#include "scrypt_platform.h" + +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#ifdef USE_OPENSSL_PBKDF2 +#include <openssl/evp.h> +#else +#include "sha256.h" +#endif +#include "sysendian.h" + +#include "crypto_scrypt.h" + +static void blkcpy(uint8_t *, uint8_t *, size_t); +static void blkxor(uint8_t *, uint8_t *, size_t); +static void salsa20_8(uint8_t[64]); +static void blockmix_salsa8(uint8_t *, uint8_t *, size_t); +static uint64_t integerify(uint8_t *, size_t); +static void smix(uint8_t *, size_t, uint64_t, uint8_t *, uint8_t *); + +static void +blkcpy(uint8_t * dest, uint8_t * src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + dest[i] = src[i]; +} + +static void +blkxor(uint8_t * dest, uint8_t * src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + dest[i] ^= src[i]; +} + +/** + * salsa20_8(B): + * Apply the salsa20/8 core to the provided block. + */ +static void +salsa20_8(uint8_t B[64]) +{ + uint32_t B32[16]; + uint32_t x[16]; + size_t i; + + /* Convert little-endian values in. */ + for (i = 0; i < 16; i++) + B32[i] = le32dec(&B[i * 4]); + + /* Compute x = doubleround^4(B32). */ + for (i = 0; i < 16; i++) + x[i] = B32[i]; + for (i = 0; i < 8; i += 2) { +#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) + /* Operate on columns. */ + x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); + x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); + + x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); + x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); + + x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); + x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); + + x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); + x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); + + /* Operate on rows. */ + x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); + x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); + + x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); + x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); + + x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); + x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); + + x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); + x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); +#undef R + } + + /* Compute B32 = B32 + x. */ + for (i = 0; i < 16; i++) + B32[i] += x[i]; + + /* Convert little-endian values out. */ + for (i = 0; i < 16; i++) + le32enc(&B[4 * i], B32[i]); +} + +/** + * blockmix_salsa8(B, Y, r): + * Compute B = BlockMix_{salsa20/8, r}(B). The input B must be 128r bytes in + * length; the temporary space Y must also be the same size. + */ +static void +blockmix_salsa8(uint8_t * B, uint8_t * Y, size_t r) +{ + uint8_t X[64]; + size_t i; + + /* 1: X <-- B_{2r - 1} */ + blkcpy(X, &B[(2 * r - 1) * 64], 64); + + /* 2: for i = 0 to 2r - 1 do */ + for (i = 0; i < 2 * r; i++) { + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &B[i * 64], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + blkcpy(&Y[i * 64], X, 64); + } + + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + for (i = 0; i < r; i++) + blkcpy(&B[i * 64], &Y[(i * 2) * 64], 64); + for (i = 0; i < r; i++) + blkcpy(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64); +} + +/** + * integerify(B, r): + * Return the result of parsing B_{2r-1} as a little-endian integer. + */ +static uint64_t +integerify(uint8_t * B, size_t r) +{ + uint8_t * X = &B[(2 * r - 1) * 64]; + + return (le64dec(X)); +} + +/** + * smix(B, r, N, V, XY): + * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; the + * temporary storage V must be 128rN bytes in length; the temporary storage + * XY must be 256r bytes in length. The value N must be a power of 2. + */ +static void +smix(uint8_t * B, size_t r, uint64_t N, uint8_t * V, uint8_t * XY) +{ + uint8_t * X = XY; + uint8_t * Y = &XY[128 * r]; + uint64_t i; + uint64_t j; + + /* 1: X <-- B */ + blkcpy(X, B, 128 * r); + + /* 2: for i = 0 to N - 1 do */ + for (i = 0; i < N; i++) { + /* 3: V_i <-- X */ + blkcpy(&V[i * (128 * r)], X, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(X, Y, r); + } + + /* 6: for i = 0 to N - 1 do */ + for (i = 0; i < N; i++) { + /* 7: j <-- Integerify(X) mod N */ + j = integerify(X, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(X, &V[j * (128 * r)], 128 * r); + blockmix_salsa8(X, Y, r); + } + + /* 10: B' <-- X */ + blkcpy(B, X, 128 * r); +} + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2. + * + * Return 0 on success; or -1 on error. + */ +int +crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, + uint8_t * buf, size_t buflen) +{ + uint8_t * B; + uint8_t * V; + uint8_t * XY; + uint32_t i; + + /* Sanity-check parameters. */ +#if SIZE_MAX > UINT32_MAX + if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { + errno = EFBIG; + goto err0; + } +#endif + if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { + errno = EFBIG; + goto err0; + } + if (((N & (N - 1)) != 0) || (N == 0)) { + errno = EINVAL; + goto err0; + } + if ((r > SIZE_MAX / 128 / p) || +#if SIZE_MAX / 256 <= UINT32_MAX + (r > SIZE_MAX / 256) || +#endif + (N > SIZE_MAX / 128 / r)) { + errno = ENOMEM; + goto err0; + } + + /* Allocate memory. */ + if ((B = malloc(128 * r * p)) == NULL) + goto err0; + if ((XY = malloc(256 * r)) == NULL) + goto err1; + if ((V = malloc(128 * r * N)) == NULL) + goto err2; + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ +#ifdef USE_OPENSSL_PBKDF2 + PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B); +#else + PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); +#endif + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { + /* 3: B_i <-- MF(B_i, N) */ + smix(&B[i * 128 * r], r, N, V, XY); + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ +#ifdef USE_OPENSSL_PBKDF2 + PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf); +#else + PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); +#endif + + /* Free memory. */ + free(V); + free(XY); + free(B); + + /* Success! */ + return (0); + +err2: + free(XY); +err1: + free(B); +err0: + /* Failure! */ + return (-1); +} diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c b/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c new file mode 100644 index 000000000..dd18f291c --- /dev/null +++ b/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c @@ -0,0 +1,378 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#include "scrypt_platform.h" + +#include <sys/types.h> +#include <sys/mman.h> + +#include <emmintrin.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#ifdef USE_OPENSSL_PBKDF2 +#include <openssl/evp.h> +#else +#include "sha256.h" +#endif +#include "sysendian.h" + +#include "crypto_scrypt.h" + +static void blkcpy(void *, void *, size_t); +static void blkxor(void *, void *, size_t); +static void salsa20_8(__m128i *); +static void blockmix_salsa8(__m128i *, __m128i *, __m128i *, size_t); +static uint64_t integerify(void *, size_t); +static void smix(uint8_t *, size_t, uint64_t, void *, void *); + +static void +blkcpy(void * dest, void * src, size_t len) +{ + __m128i * D = dest; + __m128i * S = src; + size_t L = len / 16; + size_t i; + + for (i = 0; i < L; i++) + D[i] = S[i]; +} + +static void +blkxor(void * dest, void * src, size_t len) +{ + __m128i * D = dest; + __m128i * S = src; + size_t L = len / 16; + size_t i; + + for (i = 0; i < L; i++) + D[i] = _mm_xor_si128(D[i], S[i]); +} + +/** + * salsa20_8(B): + * Apply the salsa20/8 core to the provided block. + */ +static void +salsa20_8(__m128i B[4]) +{ + __m128i X0, X1, X2, X3; + __m128i T; + size_t i; + + X0 = B[0]; + X1 = B[1]; + X2 = B[2]; + X3 = B[3]; + + for (i = 0; i < 8; i += 2) { + /* Operate on "columns". */ + T = _mm_add_epi32(X0, X3); + X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7)); + X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X1, X0); + X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); + X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X1); + X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 13)); + X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X3, X2); + X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); + X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); + + /* Rearrange data. */ + X1 = _mm_shuffle_epi32(X1, 0x93); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x39); + + /* Operate on "rows". */ + T = _mm_add_epi32(X0, X1); + X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 7)); + X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25)); + T = _mm_add_epi32(X3, X0); + X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9)); + X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23)); + T = _mm_add_epi32(X2, X3); + X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 13)); + X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19)); + T = _mm_add_epi32(X1, X2); + X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18)); + X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14)); + + /* Rearrange data. */ + X1 = _mm_shuffle_epi32(X1, 0x39); + X2 = _mm_shuffle_epi32(X2, 0x4E); + X3 = _mm_shuffle_epi32(X3, 0x93); + } + + B[0] = _mm_add_epi32(B[0], X0); + B[1] = _mm_add_epi32(B[1], X1); + B[2] = _mm_add_epi32(B[2], X2); + B[3] = _mm_add_epi32(B[3], X3); +} + +/** + * blockmix_salsa8(Bin, Bout, X, r): + * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r + * bytes in length; the output Bout must also be the same size. The + * temporary space X must be 64 bytes. + */ +static void +blockmix_salsa8(__m128i * Bin, __m128i * Bout, __m128i * X, size_t r) +{ + size_t i; + + /* 1: X <-- B_{2r - 1} */ + blkcpy(X, &Bin[8 * r - 4], 64); + + /* 2: for i = 0 to 2r - 1 do */ + for (i = 0; i < r; i++) { + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 8], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 4], X, 64); + + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 8 + 4], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[(r + i) * 4], X, 64); + } +} + +/** + * integerify(B, r): + * Return the result of parsing B_{2r-1} as a little-endian integer. + */ +static uint64_t +integerify(void * B, size_t r) +{ + uint32_t * X = (void *)((uintptr_t)(B) + (2 * r - 1) * 64); + + return (((uint64_t)(X[13]) << 32) + X[0]); +} + +/** + * smix(B, r, N, V, XY): + * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; + * the temporary storage V must be 128rN bytes in length; the temporary + * storage XY must be 256r + 64 bytes in length. The value N must be a + * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a + * multiple of 64 bytes. + */ +static void +smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY) +{ + __m128i * X = XY; + __m128i * Y = (void *)((uintptr_t)(XY) + 128 * r); + __m128i * Z = (void *)((uintptr_t)(XY) + 256 * r); + uint32_t * X32 = (void *)X; + uint64_t i, j; + size_t k; + + /* 1: X <-- B */ + for (k = 0; k < 2 * r; k++) { + for (i = 0; i < 16; i++) { + X32[k * 16 + i] = + le32dec(&B[(k * 16 + (i * 5 % 16)) * 4]); + } + } + + /* 2: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 3: V_i <-- X */ + blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(X, Y, Z, r); + + /* 3: V_i <-- X */ + blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r), + Y, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(Y, X, Z, r); + } + + /* 6: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 7: j <-- Integerify(X) mod N */ + j = integerify(X, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); + blockmix_salsa8(X, Y, Z, r); + + /* 7: j <-- Integerify(X) mod N */ + j = integerify(Y, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); + blockmix_salsa8(Y, X, Z, r); + } + + /* 10: B' <-- X */ + for (k = 0; k < 2 * r; k++) { + for (i = 0; i < 16; i++) { + le32enc(&B[(k * 16 + (i * 5 % 16)) * 4], + X32[k * 16 + i]); + } + } +} + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error. + */ +int +crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, + uint8_t * buf, size_t buflen) +{ + void * B0, * V0, * XY0; + uint8_t * B; + uint32_t * V; + uint32_t * XY; + uint32_t i; + + /* Sanity-check parameters. */ +#if SIZE_MAX > UINT32_MAX + if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { + errno = EFBIG; + goto err0; + } +#endif + if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { + errno = EFBIG; + goto err0; + } + if (((N & (N - 1)) != 0) || (N == 0)) { + errno = EINVAL; + goto err0; + } + if ((r > SIZE_MAX / 128 / p) || +#if SIZE_MAX / 256 <= UINT32_MAX + (r > (SIZE_MAX - 64) / 256) || +#endif + (N > SIZE_MAX / 128 / r)) { + errno = ENOMEM; + goto err0; + } + + /* Allocate memory. */ +#ifdef HAVE_POSIX_MEMALIGN + if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) + goto err0; + B = (uint8_t *)(B0); + if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) + goto err1; + XY = (uint32_t *)(XY0); +#ifndef MAP_ANON + if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) + goto err2; + V = (uint32_t *)(V0); +#endif +#else + if ((B0 = malloc(128 * r * p + 63)) == NULL) + goto err0; + B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); + if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) + goto err1; + XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); +#ifndef MAP_ANON + if ((V0 = malloc(128 * r * N + 63)) == NULL) + goto err2; + V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); +#endif +#endif +#ifdef MAP_ANON + if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, +#ifdef MAP_NOCORE + MAP_ANON | MAP_PRIVATE | MAP_NOCORE, +#else + MAP_ANON | MAP_PRIVATE, +#endif + -1, 0)) == MAP_FAILED) + goto err2; + V = (uint32_t *)(V0); +#endif + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ +#ifdef USE_OPENSSL_PBKDF2 + PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B); +#else + PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); +#endif + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { + /* 3: B_i <-- MF(B_i, N) */ + smix(&B[i * 128 * r], r, N, V, XY); + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ +#ifdef USE_OPENSSL_PBKDF2 + PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf); +#else + PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); +#endif + + /* Free memory. */ +#ifdef MAP_ANON + if (munmap(V0, 128 * r * N)) + goto err2; +#else + free(V0); +#endif + free(XY0); + free(B0); + + /* Success! */ + return (0); + +err2: + free(XY0); +err1: + free(B0); +err0: + /* Failure! */ + return (-1); +} diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt.h b/crypto/scrypt/lib/crypto/crypto_scrypt.h new file mode 100644 index 000000000..f72e1f4b0 --- /dev/null +++ b/crypto/scrypt/lib/crypto/crypto_scrypt.h @@ -0,0 +1,46 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#ifndef _CRYPTO_SCRYPT_H_ +#define _CRYPTO_SCRYPT_H_ + +#include <stdint.h> + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error. + */ +int crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, + uint32_t, uint32_t, uint8_t *, size_t); + +#endif /* !_CRYPTO_SCRYPT_H_ */ diff --git a/crypto/scrypt/lib/util/sysendian.h b/crypto/scrypt/lib/util/sysendian.h new file mode 100644 index 000000000..62ef31a42 --- /dev/null +++ b/crypto/scrypt/lib/util/sysendian.h @@ -0,0 +1,140 @@ +/*- + * Copyright 2007-2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#ifndef _SYSENDIAN_H_ +#define _SYSENDIAN_H_ + +#include "scrypt_platform.h" + +/* If we don't have be64enc, the <sys/endian.h> we have isn't usable. */ +#if !HAVE_DECL_BE64ENC +#undef HAVE_SYS_ENDIAN_H +#endif + +#ifdef HAVE_SYS_ENDIAN_H + +#include <sys/endian.h> + +#else + +#include <stdint.h> + +static inline uint32_t +be32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); +} + +static inline void +be32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} + +static inline uint64_t +be64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); +} + +static inline void +be64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[7] = x & 0xff; + p[6] = (x >> 8) & 0xff; + p[5] = (x >> 16) & 0xff; + p[4] = (x >> 24) & 0xff; + p[3] = (x >> 32) & 0xff; + p[2] = (x >> 40) & 0xff; + p[1] = (x >> 48) & 0xff; + p[0] = (x >> 56) & 0xff; +} + +static inline uint32_t +le32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); +} + +static inline void +le32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +static inline uint64_t +le64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); +} + +static inline void +le64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; + p[4] = (x >> 32) & 0xff; + p[5] = (x >> 40) & 0xff; + p[6] = (x >> 48) & 0xff; + p[7] = (x >> 56) & 0xff; +} +#endif /* !HAVE_SYS_ENDIAN_H */ + +#endif /* !_SYSENDIAN_H_ */ diff --git a/crypto/scrypt/patches/README b/crypto/scrypt/patches/README new file mode 100644 index 000000000..353ddbbc7 --- /dev/null +++ b/crypto/scrypt/patches/README @@ -0,0 +1,11 @@ +bionic.patch: + +Allows scrypt to compile against bionic. + +use_openssl_pbkdf2.patch: + +Uses the PBKDF2 function from OpenSSL (it uses accelerated SHA256) + +arm-neon.patch: + +Adds NEON acceleration for the Salsa20/8 mixing function. diff --git a/crypto/scrypt/patches/arm-neon.patch b/crypto/scrypt/patches/arm-neon.patch new file mode 100644 index 000000000..7197f9968 --- /dev/null +++ b/crypto/scrypt/patches/arm-neon.patch @@ -0,0 +1,437 @@ +diff --git a/lib/crypto/crypto_scrypt-neon-salsa208.h b/lib/crypto/crypto_scrypt-neon-salsa208.h +new file mode 100644 +index 0000000..a3b1019 +--- /dev/null ++++ b/lib/crypto/crypto_scrypt-neon-salsa208.h +@@ -0,0 +1,120 @@ ++/* ++ * version 20110505 ++ * D. J. Bernstein ++ * Public domain. ++ * ++ * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419 ++ */ ++ ++#define ROUNDS 8 ++static void ++salsa20_8_intrinsic(void * input) ++{ ++ int i; ++ ++ const uint32x4_t abab = {-1,0,-1,0}; ++ ++ /* ++ * This is modified since we only have one argument. Usually you'd rearrange ++ * the constant, key, and input bytes, but we just have one linear array to ++ * rearrange which is a bit easier. ++ */ ++ ++ /* ++ * Change the input to be diagonals as if it's a 4x4 matrix of 32-bit values. ++ */ ++ uint32x4_t x0x5x10x15; ++ uint32x4_t x12x1x6x11; ++ uint32x4_t x8x13x2x7; ++ uint32x4_t x4x9x14x3; ++ ++ uint32x4_t x0x1x10x11; ++ uint32x4_t x12x13x6x7; ++ uint32x4_t x8x9x2x3; ++ uint32x4_t x4x5x14x15; ++ ++ uint32x4_t x0x1x2x3; ++ uint32x4_t x4x5x6x7; ++ uint32x4_t x8x9x10x11; ++ uint32x4_t x12x13x14x15; ++ ++ x0x1x2x3 = vld1q_u8((uint8_t *) input); ++ x4x5x6x7 = vld1q_u8(16 + (uint8_t *) input); ++ x8x9x10x11 = vld1q_u8(32 + (uint8_t *) input); ++ x12x13x14x15 = vld1q_u8(48 + (uint8_t *) input); ++ ++ x0x1x10x11 = vcombine_u32(vget_low_u32(x0x1x2x3), vget_high_u32(x8x9x10x11)); ++ x4x5x14x15 = vcombine_u32(vget_low_u32(x4x5x6x7), vget_high_u32(x12x13x14x15)); ++ x8x9x2x3 = vcombine_u32(vget_low_u32(x8x9x10x11), vget_high_u32(x0x1x2x3)); ++ x12x13x6x7 = vcombine_u32(vget_low_u32(x12x13x14x15), vget_high_u32(x4x5x6x7)); ++ ++ x0x5x10x15 = vbslq_u32(abab,x0x1x10x11,x4x5x14x15); ++ x8x13x2x7 = vbslq_u32(abab,x8x9x2x3,x12x13x6x7); ++ x4x9x14x3 = vbslq_u32(abab,x4x5x14x15,x8x9x2x3); ++ x12x1x6x11 = vbslq_u32(abab,x12x13x6x7,x0x1x10x11); ++ ++ uint32x4_t start0 = x0x5x10x15; ++ uint32x4_t start1 = x12x1x6x11; ++ uint32x4_t start3 = x4x9x14x3; ++ uint32x4_t start2 = x8x13x2x7; ++ ++ /* From here on this should be the same as the SUPERCOP version. */ ++ ++ uint32x4_t diag0 = start0; ++ uint32x4_t diag1 = start1; ++ uint32x4_t diag2 = start2; ++ uint32x4_t diag3 = start3; ++ ++ uint32x4_t a0; ++ uint32x4_t a1; ++ uint32x4_t a2; ++ uint32x4_t a3; ++ ++ for (i = ROUNDS;i > 0;i -= 2) { ++ a0 = diag1 + diag0; ++ diag3 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25); ++ a1 = diag0 + diag3; ++ diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23); ++ a2 = diag3 + diag2; ++ diag1 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19); ++ a3 = diag2 + diag1; ++ diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14); ++ ++ diag3 = vextq_u32(diag3,diag3,3); ++ diag2 = vextq_u32(diag2,diag2,2); ++ diag1 = vextq_u32(diag1,diag1,1); ++ ++ a0 = diag3 + diag0; ++ diag1 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25); ++ a1 = diag0 + diag1; ++ diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23); ++ a2 = diag1 + diag2; ++ diag3 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19); ++ a3 = diag2 + diag3; ++ diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14); ++ ++ diag1 = vextq_u32(diag1,diag1,3); ++ diag2 = vextq_u32(diag2,diag2,2); ++ diag3 = vextq_u32(diag3,diag3,1); ++ } ++ ++ x0x5x10x15 = diag0 + start0; ++ x12x1x6x11 = diag1 + start1; ++ x8x13x2x7 = diag2 + start2; ++ x4x9x14x3 = diag3 + start3; ++ ++ x0x1x10x11 = vbslq_u32(abab,x0x5x10x15,x12x1x6x11); ++ x12x13x6x7 = vbslq_u32(abab,x12x1x6x11,x8x13x2x7); ++ x8x9x2x3 = vbslq_u32(abab,x8x13x2x7,x4x9x14x3); ++ x4x5x14x15 = vbslq_u32(abab,x4x9x14x3,x0x5x10x15); ++ ++ x0x1x2x3 = vcombine_u32(vget_low_u32(x0x1x10x11),vget_high_u32(x8x9x2x3)); ++ x4x5x6x7 = vcombine_u32(vget_low_u32(x4x5x14x15),vget_high_u32(x12x13x6x7)); ++ x8x9x10x11 = vcombine_u32(vget_low_u32(x8x9x2x3),vget_high_u32(x0x1x10x11)); ++ x12x13x14x15 = vcombine_u32(vget_low_u32(x12x13x6x7),vget_high_u32(x4x5x14x15)); ++ ++ vst1q_u8((uint8_t *) input,(uint8x16_t) x0x1x2x3); ++ vst1q_u8(16 + (uint8_t *) input,(uint8x16_t) x4x5x6x7); ++ vst1q_u8(32 + (uint8_t *) input,(uint8x16_t) x8x9x10x11); ++ vst1q_u8(48 + (uint8_t *) input,(uint8x16_t) x12x13x14x15); ++} +diff --git a/lib/crypto/crypto_scrypt-neon.c b/lib/crypto/crypto_scrypt-neon.c +new file mode 100644 +index 0000000..a3bf052 +--- /dev/null ++++ b/lib/crypto/crypto_scrypt-neon.c +@@ -0,0 +1,305 @@ ++/*- ++ * Copyright 2009 Colin Percival ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * This file was originally written by Colin Percival as part of the Tarsnap ++ * online backup system. ++ */ ++#include "scrypt_platform.h" ++ ++#include <machine/cpu-features.h> ++#include <arm_neon.h> ++ ++#include <errno.h> ++#include <stdint.h> ++#include <limits.h> ++#include <stdlib.h> ++#include <string.h> ++ ++#ifdef USE_OPENSSL_PBKDF2 ++#include <openssl/evp.h> ++#else ++#include "sha256.h" ++#endif ++#include "sysendian.h" ++ ++#include "crypto_scrypt.h" ++ ++#include "crypto_scrypt-neon-salsa208.h" ++ ++static void blkcpy(void *, void *, size_t); ++static void blkxor(void *, void *, size_t); ++void crypto_core_salsa208_armneon2(void *); ++static void blockmix_salsa8(uint8x16_t *, uint8x16_t *, uint8x16_t *, size_t); ++static uint64_t integerify(void *, size_t); ++static void smix(uint8_t *, size_t, uint64_t, void *, void *); ++ ++static void ++blkcpy(void * dest, void * src, size_t len) ++{ ++ uint8x16_t * D = dest; ++ uint8x16_t * S = src; ++ size_t L = len / 16; ++ size_t i; ++ ++ for (i = 0; i < L; i++) ++ D[i] = S[i]; ++} ++ ++static void ++blkxor(void * dest, void * src, size_t len) ++{ ++ uint8x16_t * D = dest; ++ uint8x16_t * S = src; ++ size_t L = len / 16; ++ size_t i; ++ ++ for (i = 0; i < L; i++) ++ D[i] = veorq_u8(D[i], S[i]); ++} ++ ++/** ++ * blockmix_salsa8(B, Y, r): ++ * Compute B = BlockMix_{salsa20/8, r}(B). The input B must be 128r bytes in ++ * length; the temporary space Y must also be the same size. ++ */ ++static void ++blockmix_salsa8(uint8x16_t * Bin, uint8x16_t * Bout, uint8x16_t * X, size_t r) ++{ ++ size_t i; ++ ++ /* 1: X <-- B_{2r - 1} */ ++ blkcpy(X, &Bin[8 * r - 4], 64); ++ ++ /* 2: for i = 0 to 2r - 1 do */ ++ for (i = 0; i < r; i++) { ++ /* 3: X <-- H(X \xor B_i) */ ++ blkxor(X, &Bin[i * 8], 64); ++ salsa20_8_intrinsic((void *) X); ++ ++ /* 4: Y_i <-- X */ ++ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ ++ blkcpy(&Bout[i * 4], X, 64); ++ ++ /* 3: X <-- H(X \xor B_i) */ ++ blkxor(X, &Bin[i * 8 + 4], 64); ++ salsa20_8_intrinsic((void *) X); ++ ++ /* 4: Y_i <-- X */ ++ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ ++ blkcpy(&Bout[(r + i) * 4], X, 64); ++ } ++} ++ ++/** ++ * integerify(B, r): ++ * Return the result of parsing B_{2r-1} as a little-endian integer. ++ */ ++static uint64_t ++integerify(void * B, size_t r) ++{ ++ uint8_t * X = (void*)((uintptr_t)(B) + (2 * r - 1) * 64); ++ ++ return (le64dec(X)); ++} ++ ++/** ++ * smix(B, r, N, V, XY): ++ * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; the ++ * temporary storage V must be 128rN bytes in length; the temporary storage ++ * XY must be 256r bytes in length. The value N must be a power of 2. ++ */ ++static void ++smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY) ++{ ++ uint8x16_t * X = XY; ++ uint8x16_t * Y = (void *)((uintptr_t)(XY) + 128 * r); ++ uint8x16_t * Z = (void *)((uintptr_t)(XY) + 256 * r); ++ uint32_t * X32 = (void *)X; ++ uint64_t i, j; ++ size_t k; ++ ++ /* 1: X <-- B */ ++ blkcpy(X, B, 128 * r); ++ ++ /* 2: for i = 0 to N - 1 do */ ++ for (i = 0; i < N; i += 2) { ++ /* 3: V_i <-- X */ ++ blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r); ++ ++ /* 4: X <-- H(X) */ ++ blockmix_salsa8(X, Y, Z, r); ++ ++ /* 3: V_i <-- X */ ++ blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r), ++ Y, 128 * r); ++ ++ /* 4: X <-- H(X) */ ++ blockmix_salsa8(Y, X, Z, r); ++ } ++ ++ /* 6: for i = 0 to N - 1 do */ ++ for (i = 0; i < N; i += 2) { ++ /* 7: j <-- Integerify(X) mod N */ ++ j = integerify(X, r) & (N - 1); ++ ++ /* 8: X <-- H(X \xor V_j) */ ++ blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); ++ blockmix_salsa8(X, Y, Z, r); ++ ++ /* 7: j <-- Integerify(X) mod N */ ++ j = integerify(Y, r) & (N - 1); ++ ++ /* 8: X <-- H(X \xor V_j) */ ++ blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r); ++ blockmix_salsa8(Y, X, Z, r); ++ } ++ ++ /* 10: B' <-- X */ ++ blkcpy(B, X, 128 * r); ++} ++ ++/** ++ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): ++ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, ++ * p, buflen) and write the result into buf. The parameters r, p, and buflen ++ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N ++ * must be a power of 2. ++ * ++ * Return 0 on success; or -1 on error. ++ */ ++int ++crypto_scrypt(const uint8_t * passwd, size_t passwdlen, ++ const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, ++ uint8_t * buf, size_t buflen) ++{ ++ void * B0, * V0, * XY0; ++ uint8_t * B; ++ uint32_t * V; ++ uint32_t * XY; ++ uint32_t i; ++ ++ /* Sanity-check parameters. */ ++#if SIZE_MAX > UINT32_MAX ++ if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { ++ errno = EFBIG; ++ goto err0; ++ } ++#endif ++ if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { ++ errno = EFBIG; ++ goto err0; ++ } ++ if (((N & (N - 1)) != 0) || (N == 0)) { ++ errno = EINVAL; ++ goto err0; ++ } ++ if ((r > SIZE_MAX / 128 / p) || ++#if SIZE_MAX / 256 <= UINT32_MAX ++ (r > SIZE_MAX / 256) || ++#endif ++ (N > SIZE_MAX / 128 / r)) { ++ errno = ENOMEM; ++ goto err0; ++ } ++ ++ /* Allocate memory. */ ++#ifdef HAVE_POSIX_MEMALIGN ++ if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) ++ goto err0; ++ B = (uint8_t *)(B0); ++ if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) ++ goto err1; ++ XY = (uint32_t *)(XY0); ++#ifndef MAP_ANON ++ if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) ++ goto err2; ++ V = (uint32_t *)(V0); ++#endif ++#else ++ if ((B0 = malloc(128 * r * p + 63)) == NULL) ++ goto err0; ++ B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); ++ if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) ++ goto err1; ++ XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); ++#ifndef MAP_ANON ++ if ((V0 = malloc(128 * r * N + 63)) == NULL) ++ goto err2; ++ V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); ++#endif ++#endif ++#ifdef MAP_ANON ++ if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, ++#ifdef MAP_NOCORE ++ MAP_ANON | MAP_PRIVATE | MAP_NOCORE, ++#else ++ MAP_ANON | MAP_PRIVATE, ++#endif ++ -1, 0)) == MAP_FAILED) ++ goto err2; ++ V = (uint32_t *)(V0); ++#endif ++ ++ /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ ++#ifdef USE_OPENSSL_PBKDF2 ++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B); ++#else ++ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); ++#endif ++ ++ /* 2: for i = 0 to p - 1 do */ ++ for (i = 0; i < p; i++) { ++ /* 3: B_i <-- MF(B_i, N) */ ++ smix(&B[i * 128 * r], r, N, V, XY); ++ } ++ ++ /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ ++#ifdef USE_OPENSSL_PBKDF2 ++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf); ++#else ++ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); ++#endif ++ ++ /* Free memory. */ ++#ifdef MAP_ANON ++ if (munmap(V0, 128 * r * N)) ++ goto err2; ++#else ++ free(V0); ++#endif ++ free(XY0); ++ free(B0); ++ ++ /* Success! */ ++ return (0); ++ ++err2: ++ free(XY0); ++err1: ++ free(B0); ++err0: ++ /* Failure! */ ++ return (-1); ++} diff --git a/crypto/scrypt/patches/use_openssl_pbkdf2.patch b/crypto/scrypt/patches/use_openssl_pbkdf2.patch new file mode 100644 index 000000000..0a1328cc0 --- /dev/null +++ b/crypto/scrypt/patches/use_openssl_pbkdf2.patch @@ -0,0 +1,80 @@ +diff --git a/lib/crypto/crypto_scrypt-ref.c b/lib/crypto/crypto_scrypt-ref.c +index 79a6f8f..60ef2aa 100644 +--- a/lib/crypto/crypto_scrypt-ref.c ++++ b/lib/crypto/crypto_scrypt-ref.c +@@ -34,7 +34,11 @@ + #include <stdlib.h> + #include <string.h> + ++#ifdef USE_OPENSSL_PBKDF2 ++#include <openssl/evp.h> ++#else + #include "sha256.h" ++#endif + #include "sysendian.h" + + #include "crypto_scrypt.h" +@@ -256,7 +260,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + goto err2; + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ ++#ifdef USE_OPENSSL_PBKDF2 ++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B); ++#else + PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); ++#endif + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { +@@ -265,7 +273,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ ++#ifdef USE_OPENSSL_PBKDF2 ++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf); ++#else + PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); ++#endif + + /* Free memory. */ + free(V); +diff --git a/lib/crypto/crypto_scrypt-sse.c b/lib/crypto/crypto_scrypt-sse.c +index 875175e..dd18f29 100644 +--- a/lib/crypto/crypto_scrypt-sse.c ++++ b/lib/crypto/crypto_scrypt-sse.c +@@ -37,7 +37,11 @@ + #include <stdlib.h> + #include <string.h> + ++#ifdef USE_OPENSSL_PBKDF2 ++#include <openssl/evp.h> ++#else + #include "sha256.h" ++#endif + #include "sysendian.h" + + #include "crypto_scrypt.h" +@@ -332,7 +336,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + #endif + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ ++#ifdef USE_OPENSSL_PBKDF2 ++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B); ++#else + PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); ++#endif + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { +@@ -341,7 +349,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen, + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ ++#ifdef USE_OPENSSL_PBKDF2 ++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf); ++#else + PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); ++#endif + + /* Free memory. */ + #ifdef MAP_ANON diff --git a/crypto/scrypt/scrypt.config b/crypto/scrypt/scrypt.config new file mode 100644 index 000000000..3ccb4d0eb --- /dev/null +++ b/crypto/scrypt/scrypt.config @@ -0,0 +1,94 @@ +CONFIGURE_ARGS="\ + \ +" + +# unneeded directories +UNNEEDED_SOURCES="\ +lib/scryptenc \ +" + +# unneeded files +UNNEEDED_SOURCES+="\ +config.h.in \ +configure \ +FORMAT \ +main.c \ +Makefile.in \ +scrypt.1 \ +lib/crypto/crypto_aesctr.c \ +lib/crypto/crypto_aesctr.h \ +lib/crypto/crypto_scrypt-nosse.c \ +lib/crypto/sha256.c \ +lib/crypto/sha256.h \ +lib/util/memlimit.c \ +lib/util/memlimit.h \ +lib/util/readpass.c \ +lib/util/readpass.h \ +lib/util/warn.c \ +lib/util/warn.h \ +" + +NEEDED_SOURCES="\ +config.h \ +lib \ +scrypt_platform.h \ +" + +SCRYPT_INCLUDES="\ +lib/crypto \ +lib/util \ +" + +SCRYPT_SOURCES="\ +lib/crypto/crypto_scrypt-ref.c \ +" + +SCRYPT_SOURCES_arm="\ +" + +SCRYPT_SOURCES_EXCLUDES_arm="\ +" + +SCRYPT_SOURCES_arm_neon="\ +lib/crypto/crypto_scrypt-neon.c \ +" + +SCRYPT_SOURCES_EXCLUDES_arm_neon="\ +lib/crypto/crypto_scrypt-ref.c \ +" + +SCRYPT_SOURCES_mips="\ +" + +SCRYPT_SOURCES_EXCLUDES_mips="\ +" + +SCRYPT_SOURCES_x86="\ +lib/crypto/crypto_scrypt-sse.c \ +" + +SCRYPT_SOURCES_EXCLUDES_x86="\ +lib/crypto/crypto_scrypt-ref.c \ +" + +SCRYPT_SOURCES_x86_64="\ +lib/crypto/crypto_scrypt-sse.c \ +" + +SCRYPT_SOURCES_EXCLUDES_x86_64="\ +lib/crypto/crypto_scrypt-ref.c \ +" + +SCRYPT_PATCHES="\ +use_openssl_pbkdf2.patch \ +arm-neon.patch \ +" + +SCRYPT_PATCHES_use_openssl_pbkdf2_SOURCES="\ +lib/crypto/crypto_scrypt-ref.c \ +" + +SCRYPT_PATCHES_bionic_SOURCES="\ +lib/crypto/crypto_scrypt-neon.c \ +lib/crypto/crypto_scrypt-neon-salsa208.h \ +" diff --git a/crypto/scrypt/scrypt.version b/crypto/scrypt/scrypt.version new file mode 100644 index 000000000..155e26061 --- /dev/null +++ b/crypto/scrypt/scrypt.version @@ -0,0 +1 @@ +SCRYPT_VERSION=1.1.6 diff --git a/crypto/scrypt/scrypt_platform.h b/crypto/scrypt/scrypt_platform.h new file mode 100644 index 000000000..5cec23631 --- /dev/null +++ b/crypto/scrypt/scrypt_platform.h @@ -0,0 +1,12 @@ +#ifndef _SCRYPT_PLATFORM_H_ +#define _SCRYPT_PLATFORM_H_ + +#if defined(CONFIG_H_FILE) +#include CONFIG_H_FILE +#elif defined(HAVE_CONFIG_H) +#include "config.h" +#else +#error Need either CONFIG_H_FILE or HAVE_CONFIG_H defined. +#endif + +#endif /* !_SCRYPT_PLATFORM_H_ */ diff --git a/crypto/scrypt/tests/Android.mk b/crypto/scrypt/tests/Android.mk new file mode 100644 index 000000000..a4562de93 --- /dev/null +++ b/crypto/scrypt/tests/Android.mk @@ -0,0 +1,23 @@ +# Build the scrypt unit tests + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + scrypt_test.cpp + +LOCAL_C_INCLUDES := \ + external/gtest/include \ + external/scrypt/lib/crypto + +LOCAL_SHARED_LIBRARIES := \ + libcrypto + +LOCAL_STATIC_LIBRARIES := \ + libscrypt_static \ + libgtest \ + libgtest_main + +LOCAL_MODULE := scrypt_test + +include $(BUILD_NATIVE_TEST) diff --git a/crypto/scrypt/tests/scrypt_test.cpp b/crypto/scrypt/tests/scrypt_test.cpp new file mode 100644 index 000000000..28334d6f5 --- /dev/null +++ b/crypto/scrypt/tests/scrypt_test.cpp @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#define LOG_TAG "scrypt_test" +#include <utils/Log.h> +#include <utils/UniquePtr.h> + +#include <gtest/gtest.h> + +#include <fstream> +#include <iostream> + +extern "C" { +#include <crypto_scrypt.h> +} + +namespace android { + +typedef struct scrypt_test_setting_t { + const char *pw, *salt; + uint32_t Nfactor, rfactor, pfactor; +} scrypt_test_setting; + +static const scrypt_test_setting post_settings[] = { + {"", "", 16, 1, 1}, + {"password", "NaCl", 1024, 8, 16}, + {"pleaseletmein", "SodiumChloride", 16384, 8, 1}, + {0, 0, 0, 0, 0} +}; + +static const uint8_t post_vectors[][64] = { + {0x77,0xd6,0x57,0x62,0x38,0x65,0x7b,0x20,0x3b,0x19,0xca,0x42,0xc1,0x8a,0x04,0x97, + 0xf1,0x6b,0x48,0x44,0xe3,0x07,0x4a,0xe8,0xdf,0xdf,0xfa,0x3f,0xed,0xe2,0x14,0x42, + 0xfc,0xd0,0x06,0x9d,0xed,0x09,0x48,0xf8,0x32,0x6a,0x75,0x3a,0x0f,0xc8,0x1f,0x17, + 0xe8,0xd3,0xe0,0xfb,0x2e,0x0d,0x36,0x28,0xcf,0x35,0xe2,0x0c,0x38,0xd1,0x89,0x06}, + {0xfd,0xba,0xbe,0x1c,0x9d,0x34,0x72,0x00,0x78,0x56,0xe7,0x19,0x0d,0x01,0xe9,0xfe, + 0x7c,0x6a,0xd7,0xcb,0xc8,0x23,0x78,0x30,0xe7,0x73,0x76,0x63,0x4b,0x37,0x31,0x62, + 0x2e,0xaf,0x30,0xd9,0x2e,0x22,0xa3,0x88,0x6f,0xf1,0x09,0x27,0x9d,0x98,0x30,0xda, + 0xc7,0x27,0xaf,0xb9,0x4a,0x83,0xee,0x6d,0x83,0x60,0xcb,0xdf,0xa2,0xcc,0x06,0x40}, + {0x70,0x23,0xbd,0xcb,0x3a,0xfd,0x73,0x48,0x46,0x1c,0x06,0xcd,0x81,0xfd,0x38,0xeb, + 0xfd,0xa8,0xfb,0xba,0x90,0x4f,0x8e,0x3e,0xa9,0xb5,0x43,0xf6,0x54,0x5d,0xa1,0xf2, + 0xd5,0x43,0x29,0x55,0x61,0x3f,0x0f,0xcf,0x62,0xd4,0x97,0x05,0x24,0x2a,0x9a,0xf9, + 0xe6,0x1e,0x85,0xdc,0x0d,0x65,0x1e,0x40,0xdf,0xcf,0x01,0x7b,0x45,0x57,0x58,0x87}, +}; + +class ScryptTest : public ::testing::Test { +}; + +TEST_F(ScryptTest, TestVectors) { + int i; + + for (i = 0; post_settings[i].pw != NULL; i++) { + uint8_t output[64]; + + scrypt_test_setting_t s = post_settings[i]; + ASSERT_EQ(0, + crypto_scrypt((const uint8_t*) s.pw, strlen(s.pw), (const uint8_t*) s.salt, + strlen(s.salt), s.Nfactor, s.rfactor, s.pfactor, output, sizeof(output))) + << "scrypt call should succeed for " << i << "; error=" << strerror(errno); + ASSERT_EQ(0, memcmp(post_vectors[i], output, sizeof(output))) + << "Should match expected output"; + } +} + +} |