diff options
Diffstat (limited to '')
40 files changed, 5750 insertions, 477 deletions
diff --git a/Android.mk b/Android.mk index aa176136d..98ea68d9b 100644 --- a/Android.mk +++ b/Android.mk @@ -354,6 +354,8 @@ endif ifeq ($(TW_INCLUDE_JB_CRYPTO), true) include $(commands_recovery_local_path)/crypto/jb/Android.mk include $(commands_recovery_local_path)/crypto/fs_mgr/Android.mk + include $(commands_recovery_local_path)/crypto/logwrapper/Android.mk + include $(commands_recovery_local_path)/crypto/scrypt/Android.mk endif ifeq ($(HAVE_SELINUX), true) include $(commands_recovery_local_path)/minzip/Android.mk diff --git a/crypto/fs_mgr/Android.mk b/crypto/fs_mgr/Android.mk index b2a341f36..f638e98c2 100644 --- a/crypto/fs_mgr/Android.mk +++ b/crypto/fs_mgr/Android.mk @@ -1,34 +1,36 @@ # 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 +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 bootable/recovery/libmincrypt/includes LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_STATIC_LIBRARY) -#include $(CLEAR_VARS) +include $(CLEAR_VARS) -#LOCAL_SRC_FILES:= fs_mgr_main.c +LOCAL_SRC_FILES:= fs_mgr_main.c -#LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include -#LOCAL_MODULE:= fs_mgr +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_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_mgr libcutils libc +LOCAL_STATIC_LIBRARIES := libfs_mgrtwrp liblogwraptwrp libcutils liblog libc libmincrypttwrp libext4_utils_static -#include $(BUILD_EXECUTABLE) +include $(BUILD_EXECUTABLE) -endif diff --git a/crypto/fs_mgr/fs_mgr.c b/crypto/fs_mgr/fs_mgr.c index 02f3ac57b..3aa9e6039 100644 --- a/crypto/fs_mgr/fs_mgr.c +++ b/crypto/fs_mgr/fs_mgr.c @@ -14,11 +14,6 @@ * limitations under the License. */ -/* TO DO: - * 1. Re-direct fsck output to the kernel log? - * - */ - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -32,17 +27,37 @@ #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; @@ -58,6 +73,12 @@ static struct flag_list mount_flags[] = { { "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 }, }; @@ -66,10 +87,27 @@ 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. @@ -100,7 +138,8 @@ static int wait_for_file(const char *filename, int timeout) return ret; } -static int parse_flags(char *flags, struct flag_list *fl, char **key_loc, +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; @@ -108,11 +147,14 @@ static int parse_flags(char *flags, struct flag_list *fl, char **key_loc, char *p; char *savep; - /* initialize key_loc to null, if we find an MF_CRYPT flag, - * then we'll set key_loc to the proper value */ - if (key_loc) { - *key_loc = NULL; + /* 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'; @@ -126,11 +168,45 @@ static int parse_flags(char *flags, struct flag_list *fl, char **key_loc, 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) && key_loc) { + 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. */ - *key_loc = strdup(strchr(p, '=') + 1); + 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; } @@ -167,7 +243,7 @@ out: * then return an empty buffer. This effectively ignores lines that are too long. * On EOF, return null. */ -static char *fs_mgr_getline(char *buf, int size, FILE *file) +static char *fs_getline(char *buf, int size, FILE *file) { int cnt = 0; int eof = 0; @@ -221,7 +297,7 @@ static char *fs_mgr_getline(char *buf, int size, FILE *file) } } -static struct fstab_rec *read_fstab(char *fstab_path) +struct fstab *fs_mgr_read_fstab(const char *fstab_path) { FILE *fstab_file; int cnt, entries; @@ -229,8 +305,9 @@ static struct fstab_rec *read_fstab(char *fstab_path) char line[256]; const char *delim = " \t"; char *save_ptr, *p; - struct fstab_rec *fstab; - char *key_loc; + 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]; @@ -241,7 +318,7 @@ static struct fstab_rec *read_fstab(char *fstab_path) } entries = 0; - while (fs_mgr_getline(line, sizeof(line), fstab_file)) { + 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') { @@ -263,12 +340,16 @@ static struct fstab_rec *read_fstab(char *fstab_path) return 0; } - fstab = calloc(entries + 1, sizeof(struct fstab_rec)); + /* 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_mgr_getline(line, sizeof(line), fstab_file)) { + 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') { @@ -297,41 +378,47 @@ static struct fstab_rec *read_fstab(char *fstab_path) ERROR("Error parsing mount source\n"); return 0; } - fstab[cnt].blk_dev = strdup(p); + fstab->recs[cnt].blk_device = strdup(p); if (!(p = strtok_r(NULL, delim, &save_ptr))) { - ERROR("Error parsing mnt_point\n"); + ERROR("Error parsing mount_point\n"); return 0; } - fstab[cnt].mnt_point = strdup(p); + fstab->recs[cnt].mount_point = strdup(p); if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing fs_type\n"); return 0; } - fstab[cnt].type = strdup(p); + 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[cnt].flags = parse_flags(p, mount_flags, 0, tmp_fs_options, FS_OPTIONS_LEN); + 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[cnt].fs_options = strdup(tmp_fs_options); + fstab->recs[cnt].fs_options = strdup(tmp_fs_options); } else { - fstab[cnt].fs_options = NULL; + fstab->recs[cnt].fs_options = NULL; } if (!(p = strtok_r(NULL, delim, &save_ptr))) { ERROR("Error parsing fs_mgr_options\n"); return 0; } - fstab[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, &key_loc, 0, 0); - fstab[cnt].key_loc = key_loc; - + 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); @@ -339,46 +426,76 @@ static struct fstab_rec *read_fstab(char *fstab_path) return fstab; } -static void free_fstab(struct fstab_rec *fstab) +void fs_mgr_free_fstab(struct fstab *fstab) { - int i = 0; + int i; - while (fstab[i].blk_dev) { - /* Free the pointers return by strdup(3) */ - free(fstab[i].blk_dev); - free(fstab[i].mnt_point); - free(fstab[i].type); - free(fstab[i].fs_options); - free(fstab[i].key_loc); + 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 actual fstab array created by calloc(3) */ + /* 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_dev, char *type) +static void check_fs(char *blk_device, char *fs_type, char *target) { - pid_t pid; 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(type, "ext2") || !strcmp(type, "ext3") || !strcmp(type, "ext4")) { - INFO("Running %s on %s\n", E2FSCK_BIN, blk_dev); - pid = fork(); - if (pid > 0) { - /* Parent, wait for the child to return */ - waitpid(pid, &status, 0); - } else if (pid == 0) { - /* child, run checker */ - execlp(E2FSCK_BIN, E2FSCK_BIN, "-y", blk_dev, (char *)NULL); - - /* Only gets here on error */ - ERROR("Cannot run fs_mgr binary %s\n", E2FSCK_BIN); - } else { + 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("Fork failed trying to run %s\n", E2FSCK_BIN); + ERROR("Failed trying to run %s\n", E2FSCK_BIN); } } @@ -396,6 +513,43 @@ static void remove_trailing_slashes(char *n) } } +/* + * 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; @@ -416,49 +570,71 @@ static int fs_match(char *in1, char *in2) return ret; } -int fs_mgr_mount_all(char *fstab_file) +int fs_mgr_mount_all(struct fstab *fstab) { int i = 0; int encrypted = 0; int ret = -1; int mret; - struct fstab_rec *fstab = 0; - if (!(fstab = read_fstab(fstab_file))) { + if (!fstab) { return ret; } - for (i = 0; fstab[i].blk_dev; i++) { - if (fstab[i].fs_mgr_flags & MF_WAIT) { - wait_for_file(fstab[i].blk_dev, WAIT_TIMEOUT); + 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; } - if (fstab[i].fs_mgr_flags & MF_CHECK) { - check_fs(fstab[i].blk_dev, fstab[i].type); + /* 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; } - mret = mount(fstab[i].blk_dev, fstab[i].mnt_point, fstab[i].type, - fstab[i].flags, fstab[i].fs_options); + 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[i].fs_mgr_flags & MF_CRYPT) && !partition_wiped(fstab[i].blk_dev)) { + 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[i].mnt_point, "tmpfs", + 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[i].mnt_point); + fstab->recs[i].mount_point); goto out; } encrypted = 1; } else { ERROR("Cannot mount filesystem on %s at %s\n", - fstab[i].blk_dev, fstab[i].mnt_point); + fstab->recs[i].blk_device, fstab->recs[i].mount_point); goto out; } } @@ -470,49 +646,65 @@ int fs_mgr_mount_all(char *fstab_file) } out: - free_fstab(fstab); return ret; } -/* If tmp_mnt_point is non-null, mount the filesystem there. This is for the +/* 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(char *fstab_file, char *n_name, char *n_blk_dev, char *tmp_mnt_point) +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; - struct fstab_rec *fstab = 0; char *m; - if (!(fstab = read_fstab(fstab_file))) { + if (!fstab) { return ret; } - for (i = 0; fstab[i].blk_dev; i++) { - if (!fs_match(fstab[i].mnt_point, n_name)) { + 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[i].fs_mgr_flags & MF_WAIT) { - wait_for_file(fstab[i].blk_dev, WAIT_TIMEOUT); + 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[i].fs_mgr_flags & MF_CHECK) && strcmp("ext4", fstab[i].type) != 0) { - check_fs(fstab[i].blk_dev, fstab[i].type); + 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_mnt_point) { - m = tmp_mnt_point; + if (tmp_mount_point) { + m = tmp_mount_point; } else { - m = fstab[i].mnt_point; + m = fstab->recs[i].mount_point; } - if (mount(n_blk_dev, m, fstab[i].type, - fstab[i].flags, fstab[i].fs_options)) { + 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_dev, m); + n_blk_device, m); goto out; } else { ret = 0; @@ -521,10 +713,9 @@ int fs_mgr_do_mount(char *fstab_file, char *n_name, char *n_blk_dev, char *tmp_m } /* We didn't find a match, say so and return an error */ - ERROR("Cannot find mount point %s in fstab\n", fstab[i].mnt_point); + ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point); out: - free_fstab(fstab); return ret; } @@ -547,65 +738,211 @@ int fs_mgr_do_tmpfs_mount(char *n_name) return 0; } -int fs_mgr_unmount_all(char *fstab_file) +int fs_mgr_unmount_all(struct fstab *fstab) { int i = 0; int ret = 0; - struct fstab_rec *fstab = 0; - if (!(fstab = read_fstab(fstab_file))) { + if (!fstab) { return -1; } - while (fstab[i].blk_dev) { - if (umount(fstab[i].mnt_point)) { - ERROR("Cannot unmount filesystem at %s\n", fstab[i].mnt_point); + 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++; } - free_fstab(fstab); 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_dev 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(char *fstab_file, char *key_loc, char *real_blk_dev, int size) +int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size) { int i = 0; - struct fstab_rec *fstab = 0; - if (!(fstab = read_fstab(fstab_file))) { + if (!fstab) { return -1; } /* Initialize return values to null strings */ if (key_loc) { *key_loc = '\0'; } - if (real_blk_dev) { - *real_blk_dev = '\0'; + if (real_blk_device) { + *real_blk_device = '\0'; } /* Look for the encryptable partition to find the data */ - for (i = 0; fstab[i].blk_dev; i++) { - if (!(fstab[i].fs_mgr_flags & MF_CRYPT)) { + 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[i].key_loc, size); + strlcpy(key_loc, fstab->recs[i].key_loc, size); } - if (real_blk_dev) { - strlcpy(real_blk_dev, fstab[i].blk_dev, size); + if (real_blk_device) { + strlcpy(real_blk_device, fstab->recs[i].blk_device, size); } break; } - free_fstab(fstab); 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 index 81febf124..4bde4a1da 100644 --- a/crypto/fs_mgr/fs_mgr_main.c +++ b/crypto/fs_mgr/fs_mgr_main.c @@ -82,7 +82,8 @@ int main(int argc, char *argv[]) int n_flag=0; char *n_name; char *n_blk_dev; - char *fstab; + char *fstab_file; + struct fstab *fstab; klog_init(); klog_set_level(6); @@ -90,7 +91,9 @@ int main(int argc, char *argv[]) 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 = argv[argc - 1]; + fstab_file = argv[argc - 1]; + + fstab = fs_mgr_read_fstab(fstab_file); if (a_flag) { return fs_mgr_mount_all(fstab); @@ -103,6 +106,8 @@ int main(int argc, char *argv[]) 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 index 175fdabd2..59ffd785c 100644 --- a/crypto/fs_mgr/fs_mgr_priv.h +++ b/crypto/fs_mgr/fs_mgr_priv.h @@ -25,17 +25,7 @@ #define CRYPTO_TMPFS_OPTIONS "size=128m,mode=0771,uid=1000,gid=1000" -struct fstab_rec { - char *blk_dev; - char *mnt_point; - char *type; - unsigned long flags; - char *fs_options; - int fs_mgr_flags; - char *key_loc; -}; - -#define WAIT_TIMEOUT 5 +#define WAIT_TIMEOUT 20 /* fstab has the following format: * @@ -59,8 +49,8 @@ struct fstab_rec { * 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. I can either be a path + * 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. @@ -72,9 +62,24 @@ struct fstab_rec { * */ -#define MF_WAIT 0x1 -#define MF_CHECK 0x2 -#define MF_CRYPT 0x4 +#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 index 76abb8332..0f90c32f1 100644 --- a/crypto/fs_mgr/include/fs_mgr.h +++ b/crypto/fs_mgr/include/fs_mgr.h @@ -17,11 +17,56 @@ #ifndef __CORE_FS_MGR_H #define __CORE_FS_MGR_H -int fs_mgr_mount_all(char *fstab_file); -int fs_mgr_do_mount(char *fstab_file, char *n_name, char *n_blk_dev, char *tmp_mnt_point); +#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(char *fstab_file); -int fs_mgr_get_crypt_info(char *fstab_file, char *key_loc, char *real_blk_dev, int size); +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/jb/Android.mk b/crypto/jb/Android.mk index eebfcf0bd..dc458fd14 100644 --- a/crypto/jb/Android.mk +++ b/crypto/jb/Android.mk @@ -7,9 +7,9 @@ LOCAL_MODULE_TAGS := eng LOCAL_MODULES_TAGS = optional LOCAL_CFLAGS = LOCAL_SRC_FILES = cryptfs.c -LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include -LOCAL_SHARED_LIBRARIES += libc liblog libcutils libcrypto -LOCAL_STATIC_LIBRARIES += libfs_mgrtwrp +LOCAL_C_INCLUDES += system/extras/ext4_utils external/openssl/include bootable/recovery/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
\ No newline at end of file diff --git a/crypto/jb/cryptfs.c b/crypto/jb/cryptfs.c index be6c476b8..4e5706b64 100644 --- a/crypto/jb/cryptfs.c +++ b/crypto/jb/cryptfs.c @@ -21,6 +21,7 @@ */ #include <sys/types.h> +#include <sys/wait.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> @@ -35,17 +36,19 @@ #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 "../fs_mgr/include/fs_mgr.h" +#include <fs_mgr.h> #include "cryptfs.h" #define LOG_TAG "Cryptfs" -#include "cutils/android_reboot.h" #include "cutils/log.h" #include "cutils/properties.h" +#include "cutils/android_reboot.h" #include "hardware_legacy/power.h" -//#include "VolumeManager.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" @@ -59,14 +62,29 @@ #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_data_blkdev; static char *saved_mount_point; static int master_key_saved = 0; -#define FSTAB_PREFIX "/fstab." -static char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)]; +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) { @@ -82,6 +100,56 @@ static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, u } } +/** + * 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; @@ -89,17 +157,17 @@ static unsigned int get_fs_size(char *dev) off64_t len; if ((fd = open(dev, O_RDONLY)) < 0) { - SLOGE("Cannot open device to get filesystem size "); + printf("Cannot open device to get filesystem size "); return 0; } if (lseek64(fd, 1024, SEEK_SET) < 0) { - SLOGE("Cannot seek to superblock"); + printf("Cannot seek to superblock"); return 0; } if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) { - SLOGE("Cannot read superblock"); + printf("Cannot read superblock"); return 0; } @@ -124,108 +192,102 @@ static unsigned int get_blkdev_size(int fd) return nr_sec; } -/* Get and cache the name of the fstab file so we don't - * keep talking over the socket to the property service. - */ -static char *get_fstab_filename(void) +static int get_crypt_ftr_info(char **metadata_fname, off64_t *off) { - if (fstab_filename[0] == 0) { - strcpy(fstab_filename, FSTAB_PREFIX); - property_get("ro.hardware", fstab_filename + sizeof(FSTAB_PREFIX) - 1, ""); + 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 fstab_filename; + 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(char *real_blk_name, struct crypt_mnt_ftr *crypt_ftr, - unsigned char *key, unsigned char *salt) +static int put_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr) { int fd; unsigned int nr_sec, cnt; - off64_t off; + /* starting_off is set to the SEEK_SET offset + * where the crypto structure starts + */ + off64_t starting_off; int rc = -1; - char *fname; - char key_loc[PROPERTY_VALUE_MAX]; + char *fname = NULL; struct stat statbuf; - fs_mgr_get_crypt_info(get_fstab_filename(), key_loc, 0, sizeof(key_loc)); - - if (!strcmp(key_loc, KEY_IN_FOOTER)) { - fname = real_blk_name; - if ( (fd = open(fname, O_RDWR)) < 0) { - SLOGE("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) { - SLOGE("Cannot seek to real block device footer\n"); - goto errout; - } - } else if (key_loc[0] == '/') { - fname = key_loc; - if ( (fd = open(fname, O_RDWR | O_CREAT, 0600)) < 0) { - SLOGE("Cannot open footer file %s\n", fname); - return -1; - } - } else { - SLOGE("Unexpected value for crypto key location\n"); - return -1;; + if (get_crypt_ftr_info(&fname, &starting_off)) { + printf("Unable to get crypt_ftr_info\n"); + return -1; } - - if ((cnt = write(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) { - SLOGE("Cannot write real block device footer\n"); - goto errout; + if (fname[0] != '/') { + printf("Unexpected value for crypto key location\n"); + return -1; } - - if (key) { - if (crypt_ftr->keysize != KEY_LEN_BYTES) { - SLOGE("Keysize of %d bits not supported for real block device %s\n", - crypt_ftr->keysize*8, fname); - goto errout; - } - - if ( (cnt = write(fd, key, crypt_ftr->keysize)) != crypt_ftr->keysize) { - SLOGE("Cannot write key for real block device %s\n", fname); - goto errout; - } + if ( (fd = open(fname, O_RDWR | O_CREAT, 0600)) < 0) { + printf("Cannot open footer file %s for put\n", fname); + return -1; } - if (salt) { - /* Compute the offset from the last write to the salt */ - off = KEY_TO_SALT_PADDING; - if (! key) - off += crypt_ftr->keysize; - - if (lseek64(fd, off, SEEK_CUR) == -1) { - SLOGE("Cannot seek to real block device salt \n"); - 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 = write(fd, salt, SALT_LEN)) != SALT_LEN) { - SLOGE("Cannot write salt for real block device %s\n", fname); - 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) && (key_loc[0] == '/')) { + if (S_ISREG(statbuf.st_mode)) { if (ftruncate(fd, 0x4000)) { - SLOGE("Cannot set footer file size\n", fname); + printf("Cannot set footer file size\n", fname); goto errout; } } @@ -239,111 +301,145 @@ errout: } -static int get_crypt_ftr_and_key(char *real_blk_name, struct crypt_mnt_ftr *crypt_ftr, - unsigned char *key, unsigned char *salt) +static inline int unix_read(int fd, void* buff, int len) { - int fd; - unsigned int nr_sec, cnt; - off64_t off; - int rc = -1; - char key_loc[PROPERTY_VALUE_MAX]; - char *fname; - struct stat statbuf; - - fs_mgr_get_crypt_info(get_fstab_filename(), key_loc, 0, sizeof(key_loc)); + return TEMP_FAILURE_RETRY(read(fd, buff, len)); +} - if (!strcmp(key_loc, KEY_IN_FOOTER)) { - fname = real_blk_name; - if ( (fd = open(fname, O_RDONLY)) < 0) { - SLOGE("Cannot open real block device %s\n", fname); - return -1; - } +static inline int unix_write(int fd, const void* buff, int len) +{ + return TEMP_FAILURE_RETRY(write(fd, buff, len)); +} - if ( (nr_sec = get_blkdev_size(fd)) == 0) { - SLOGE("Cannot get size of block device %s\n", fname); - goto errout; - } +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; +} - /* 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; +/* 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); - if (lseek64(fd, off, SEEK_SET) == -1) { - SLOGE("Cannot seek to real block device footer\n"); - goto errout; + /* 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; } - } else if (key_loc[0] == '/') { - fname = key_loc; - if ( (fd = open(fname, O_RDONLY)) < 0) { - SLOGE("Cannot open footer file %s\n", fname); - return -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; } - /* Make sure it's 16 Kbytes in length */ - fstat(fd, &statbuf); - if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) { - SLOGE("footer file %s is not the expected size!\n", fname); - goto errout; + 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)); } - } else { - SLOGE("Unexpected value for crypto key location\n"); - return -1;; - } +} - if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) { - SLOGE("Cannot read real block device footer\n"); - goto errout; - } - if (crypt_ftr->magic != CRYPT_MNT_MAGIC) { - SLOGE("Bad magic for real block device %s\n", fname); - goto errout; - } +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 (crypt_ftr->major_version != 1) { - SLOGE("Cannot understand major version %d real block device footer\n", - crypt_ftr->major_version); - goto errout; + if (get_crypt_ftr_info(&fname, &starting_off)) { + printf("Unable to get crypt_ftr_info\n"); + return -1; } - - if (crypt_ftr->minor_version != 0) { - SLOGW("Warning: crypto footer minor version %d, expected 0, continuing...\n", - crypt_ftr->minor_version); + 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; } - 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) { - SLOGE("Cannot seek to start of key\n"); - goto errout; - } + /* 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; } - if (crypt_ftr->keysize != KEY_LEN_BYTES) { - SLOGE("Keysize of %d bits not supported for real block device %s\n", - crypt_ftr->keysize * 8, fname); + /* 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, key, crypt_ftr->keysize)) != crypt_ftr->keysize) { - SLOGE("Cannot read key for real block device %s\n", fname); + 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 (lseek64(fd, KEY_TO_SALT_PADDING, SEEK_CUR) == -1) { - SLOGE("Cannot seek to real block device salt\n"); + if (crypt_ftr->magic != CRYPT_MNT_MAGIC) { + printf("Bad magic for real block device %s\n", fname); goto errout; } - if ( (cnt = read(fd, salt, SALT_LEN)) != SALT_LEN) { - SLOGE("Cannot read salt for real block device %s\n", fname); + 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; @@ -352,6 +448,227 @@ errout: 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 */ @@ -375,6 +692,86 @@ void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize, } +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) { @@ -385,10 +782,14 @@ static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char 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 ) { - SLOGE("Cannot open device-mapper\n"); + printf("Cannot open device-mapper\n"); goto errout; } @@ -396,47 +797,43 @@ static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); if (ioctl(fd, DM_DEV_CREATE, io)) { - SLOGE("Cannot create dm-crypt device\n"); + 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)) { - SLOGE("Cannot retrieve dm-crypt device status\n"); + 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); - 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; + 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"); + } + } - if (ioctl(fd, DM_TABLE_LOAD, io)) { - SLOGE("Cannot load dm-crypt mapping table.\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, 4096, name, 0); + ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); if (ioctl(fd, DM_DEV_SUSPEND, io)) { - SLOGE("Cannot resume the dm-crypt device\n"); + printf("Cannot resume the dm-crypt device\n"); goto errout; } @@ -457,7 +854,7 @@ static int delete_crypto_blk_dev(char *name) int retval = -1; if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) { - SLOGE("Cannot open device-mapper\n"); + printf("Cannot open device-mapper\n"); goto errout; } @@ -465,7 +862,7 @@ static int delete_crypto_blk_dev(char *name) ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0); if (ioctl(fd, DM_DEV_REMOVE, io)) { - SLOGE("Cannot remove dm-crypt device\n"); + printf("Cannot remove dm-crypt device\n"); goto errout; } @@ -479,27 +876,40 @@ errout: } -static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey) -{ +static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey, void *params) { /* Turn the password into a key and IV that can decrypt the master key */ PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN, HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey); } +static void scrypt(char *passwd, unsigned char *salt, unsigned char *ikey, void *params) { + struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params; + + int N = 1 << ftr->N_factor; + int r = 1 << ftr->r_factor; + int p = 1 << ftr->p_factor; + + /* Turn the password into a key and IV that can decrypt the master key */ + crypto_scrypt((unsigned char *) passwd, strlen(passwd), salt, SALT_LEN, N, r, p, ikey, + KEY_LEN_BYTES + IV_LEN_BYTES); +} + static int encrypt_master_key(char *passwd, unsigned char *salt, unsigned char *decrypted_master_key, - unsigned char *encrypted_master_key) + unsigned char *encrypted_master_key, + struct crypt_mnt_ftr *crypt_ftr) { unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */ EVP_CIPHER_CTX e_ctx; int encrypted_len, final_len; /* Turn the password into a key and IV that can decrypt the master key */ - pbkdf2(passwd, salt, ikey); - + get_device_scrypt_params(crypt_ftr); + scrypt(passwd, salt, ikey, crypt_ftr); + /* Initialize the decryption engine */ if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) { - SLOGE("EVP_EncryptInit failed\n"); + printf("EVP_EncryptInit failed\n"); return -1; } EVP_CIPHER_CTX_set_padding(&e_ctx, 0); /* Turn off padding as our data is block aligned */ @@ -507,16 +917,16 @@ static int encrypt_master_key(char *passwd, unsigned char *salt, /* Encrypt the master key */ if (! EVP_EncryptUpdate(&e_ctx, encrypted_master_key, &encrypted_len, decrypted_master_key, KEY_LEN_BYTES)) { - SLOGE("EVP_EncryptUpdate failed\n"); + printf("EVP_EncryptUpdate failed\n"); return -1; } if (! EVP_EncryptFinal(&e_ctx, encrypted_master_key + encrypted_len, &final_len)) { - SLOGE("EVP_EncryptFinal failed\n"); + printf("EVP_EncryptFinal failed\n"); return -1; } if (encrypted_len + final_len != KEY_LEN_BYTES) { - SLOGE("EVP_Encryption length check failed with %d, %d bytes\n", encrypted_len, final_len); + printf("EVP_Encryption length check failed with %d, %d bytes\n", encrypted_len, final_len); return -1; } else { return 0; @@ -525,14 +935,15 @@ static int encrypt_master_key(char *passwd, unsigned char *salt, static int decrypt_master_key(char *passwd, unsigned char *salt, unsigned char *encrypted_master_key, - unsigned char *decrypted_master_key) + unsigned char *decrypted_master_key, + kdf_func kdf, void *kdf_params) { unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */ EVP_CIPHER_CTX d_ctx; int decrypted_len, final_len; /* Turn the password into a key and IV that can decrypt the master key */ - pbkdf2(passwd, salt, ikey); + kdf(passwd, salt, ikey, kdf_params); /* Initialize the decryption engine */ if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) { @@ -555,8 +966,47 @@ static int decrypt_master_key(char *passwd, unsigned char *salt, } } -static int create_encrypted_random_key(char *passwd, unsigned char *master_key, unsigned char *salt) +static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params) +{ + if (ftr->kdf_type == KDF_SCRYPT) { + *kdf = scrypt; + *kdf_params = ftr; + } else { + *kdf = pbkdf2; + *kdf_params = NULL; + } +} + +static int decrypt_master_key_and_upgrade(char *passwd, unsigned char *decrypted_master_key, + struct crypt_mnt_ftr *crypt_ftr) { + kdf_func kdf; + void *kdf_params; + int ret; + + get_kdf_func(crypt_ftr, &kdf, &kdf_params); + ret = decrypt_master_key(passwd, crypt_ftr->salt, crypt_ftr->master_key, decrypted_master_key, kdf, + kdf_params); + if (ret != 0) { + 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; @@ -569,7 +1019,7 @@ static int create_encrypted_random_key(char *passwd, unsigned char *master_key, close(fd); /* Now encrypt it with the password */ - return encrypt_master_key(passwd, salt, key_buf, master_key); + return encrypt_master_key(passwd, salt, key_buf, master_key, crypt_ftr); } static int wait_and_unmount(char *mountpoint) @@ -594,17 +1044,17 @@ static int wait_and_unmount(char *mountpoint) } if (i < WAIT_UNMOUNT_COUNT) { - SLOGD("unmounting %s succeeded\n", mountpoint); + printf("unmounting %s succeeded\n", mountpoint); rc = 0; } else { - SLOGE("unmounting %s failed\n", mountpoint); + printf("unmounting %s failed\n", mountpoint); rc = -1; } return rc; } -#define DATA_PREP_TIMEOUT 100 +#define DATA_PREP_TIMEOUT 200 static int prep_data_fs(void) { int i; @@ -612,9 +1062,9 @@ static int prep_data_fs(void) /* Do the prep of the /data filesystem */ property_set("vold.post_fs_data_done", "0"); property_set("vold.decrypt", "trigger_post_fs_data"); - SLOGD("Just triggered post_fs_data\n"); + printf("Just triggered post_fs_data\n"); - /* Wait a max of 25 seconds, hopefully it takes much less */ + /* Wait a max of 50 seconds, hopefully it takes much less */ for (i=0; i<DATA_PREP_TIMEOUT; i++) { char p[PROPERTY_VALUE_MAX]; @@ -627,9 +1077,10 @@ static int prep_data_fs(void) } if (i == DATA_PREP_TIMEOUT) { /* Ugh, we failed to prep /data in time. Bail. */ + printf("post_fs_data timed out!\n"); return -1; } else { - SLOGD("post_fs_data done\n"); + printf("post_fs_data done\n"); return 0; } } @@ -647,12 +1098,12 @@ int cryptfs_restart(void) /* Validate that it's OK to call this routine */ if (! master_key_saved) { - SLOGE("Encrypted filesystem not validated, aborting"); + printf("Encrypted filesystem not validated, aborting"); return -1; } if (restart_successful) { - SLOGE("System already restarted with encrypted disk, aborting"); + printf("System already restarted with encrypted disk, aborting"); return -1; } @@ -674,7 +1125,14 @@ int cryptfs_restart(void) * set to trigger_reset_main. */ property_set("vold.decrypt", "trigger_reset_main"); - SLOGD("Just asked init to shut down class main\n"); + 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. @@ -682,13 +1140,13 @@ int cryptfs_restart(void) property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, ""); if (strlen(crypto_blkdev) == 0) { - SLOGE("fs_crypto_blkdev not set\n"); + 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(get_fstab_filename(), DATA_MNT_POINT, crypto_blkdev, 0); + fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, 0); property_set("vold.decrypt", "trigger_load_persist_props"); /* Create necessary paths on /data */ @@ -698,7 +1156,7 @@ int cryptfs_restart(void) /* startup service classes main and late_start */ property_set("vold.decrypt", "trigger_restart_framework"); - SLOGD("Just triggered restart_framework\n"); + printf("Just triggered restart_framework\n"); /* Give it a few moments to get started */ sleep(1); @@ -714,22 +1172,17 @@ int cryptfs_restart(void) static int do_crypto_complete(char *mount_point) { struct crypt_mnt_ftr crypt_ftr; - unsigned char encrypted_master_key[32]; - unsigned char salt[SALT_LEN]; - char real_blkdev[MAXPATHLEN]; char encrypted_state[PROPERTY_VALUE_MAX]; char key_loc[PROPERTY_VALUE_MAX]; property_get("ro.crypto.state", encrypted_state, ""); if (strcmp(encrypted_state, "encrypted") ) { - SLOGE("not running with encryption, aborting"); + printf("not running with encryption, aborting"); return 1; } - fs_mgr_get_crypt_info(get_fstab_filename(), 0, real_blkdev, sizeof(real_blkdev)); - - if (get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt)) { - fs_mgr_get_crypt_info(get_fstab_filename(), key_loc, 0, sizeof(key_loc)); + 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. @@ -739,16 +1192,16 @@ static int do_crypto_complete(char *mount_point) * device" screen. */ if ((key_loc[0] == '/') && (access("key_loc", F_OK) == -1)) { - SLOGE("master key file does not exist, aborting"); + printf("master key file does not exist, aborting"); return 1; } else { - SLOGE("Error getting crypt footer and key\n"); + printf("Error getting crypt footer and key\n"); return -1; } } if (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS) { - SLOGE("Encryption process didn't finish successfully\n"); + 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 */ } @@ -761,42 +1214,43 @@ 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 encrypted_master_key[32], decrypted_master_key[32]; - unsigned char salt[SALT_LEN]; + 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") ) { - SLOGE("encrypted fs already validated or not running with encryption, aborting"); + printf("encrypted fs already validated or not running with encryption, aborting"); return -1; } - fs_mgr_get_crypt_info(get_fstab_filename(), 0, real_blkdev, sizeof(real_blkdev)); + fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev)); - if (get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt)) { - SLOGE("Error getting crypt footer and key\n"); + if (get_crypt_ftr_and_key(&crypt_ftr)) { + printf("Error getting crypt footer and key\n"); return -1; } - SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr.fs_size); + 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); + 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)) { - SLOGE("Error creating decrypted block device\n"); + printf("Error creating decrypted block device\n"); return -1; } - /* If init detects an encrypted filesystme, it writes a file for each such + /* 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 @@ -805,8 +1259,8 @@ static int test_mount_encrypted_fs(char *passwd, char *mount_point, char *label) */ sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point); mkdir(tmp_mount_point, 0755); - if (fs_mgr_do_mount(get_fstab_filename(), DATA_MNT_POINT, crypto_blkdev, tmp_mount_point)) { - SLOGE("Error temp mounting decrypted block device\n"); + 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 { @@ -818,7 +1272,7 @@ static int test_mount_encrypted_fs(char *passwd, char *mount_point, char *label) } if (orig_failed_decrypt_count != crypt_ftr.failed_decrypt_count) { - put_crypt_ftr_and_key(real_blkdev, &crypt_ftr, 0, 0); + put_crypt_ftr_and_key(&crypt_ftr); } if (crypt_ftr.failed_decrypt_count) { @@ -835,7 +1289,6 @@ static int test_mount_encrypted_fs(char *passwd, char *mount_point, char *label) * the key when we want to change the password on it. */ memcpy(saved_master_key, decrypted_master_key, KEY_LEN_BYTES); - saved_data_blkdev = strdup(real_blkdev); saved_mount_point = strdup(mount_point); master_key_saved = 1; rc = 0; @@ -864,21 +1317,19 @@ int cryptfs_setup_volume(const char *label, int major, int minor, { char real_blkdev[MAXPATHLEN], crypto_blkdev[MAXPATHLEN]; struct crypt_mnt_ftr sd_crypt_ftr; - unsigned char key[32], salt[32]; struct stat statbuf; int nr_sec, fd; sprintf(real_blkdev, "/dev/block/vold/%d:%d", major, minor); - /* Just want the footer, but gotta get it all */ - get_crypt_ftr_and_key(saved_data_blkdev, &sd_crypt_ftr, key, salt); + 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) { - SLOGE("Cannot get size of volume %s\n", real_blkdev); + printf("Cannot get size of volume %s\n", real_blkdev); return -1; } @@ -901,9 +1352,24 @@ int cryptfs_crypto_complete(void) return do_crypto_complete("/data"); } +#define FSTAB_PREFIX "/fstab." + int cryptfs_check_passwd(char *passwd) { int rc = -1; + char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)]; + char propbuf[PROPERTY_VALUE_MAX]; + int i; + int flags; + + 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"); @@ -914,32 +1380,28 @@ 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 encrypted_master_key[32], decrypted_master_key[32]; - unsigned char salt[SALT_LEN]; - char real_blkdev[MAXPATHLEN]; + 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") ) { - SLOGE("device not encrypted, aborting"); + printf("device not encrypted, aborting"); return -2; } if (!master_key_saved) { - SLOGE("encrypted fs not yet mounted, aborting"); + printf("encrypted fs not yet mounted, aborting"); return -1; } if (!saved_mount_point) { - SLOGE("encrypted fs failed to save mount point, aborting"); + printf("encrypted fs failed to save mount point, aborting"); return -1; } - fs_mgr_get_crypt_info(get_fstab_filename(), 0, real_blkdev, sizeof(real_blkdev)); - - if (get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt)) { - SLOGE("Error getting crypt footer and key\n"); + if (get_crypt_ftr_and_key(&crypt_ftr)) { + printf("Error getting crypt footer and key\n"); return -1; } @@ -947,7 +1409,7 @@ int cryptfs_verify_passwd(char *passwd) /* If the device has no password, then just say the password is valid */ rc = 0; } else { - decrypt_master_key(passwd, salt, encrypted_master_key, decrypted_master_key); + decrypt_master_key_and_upgrade(passwd, decrypted_master_key, &crypt_ftr); if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) { /* They match, the password is correct */ rc = 0; @@ -968,58 +1430,29 @@ int cryptfs_verify_passwd(char *passwd) */ 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 = 1; - ftr->minor_version = 0; + ftr->major_version = CURRENT_MAJOR_VERSION; + ftr->minor_version = CURRENT_MINOR_VERSION; ftr->ftr_size = sizeof(struct crypt_mnt_ftr); - ftr->flags = 0; ftr->keysize = KEY_LEN_BYTES; - ftr->spare1 = 0; - ftr->fs_size = 0; - ftr->failed_decrypt_count = 0; - ftr->crypto_type_name[0] = '\0'; -} -static int cryptfs_enable_wipe(char *crypto_blkdev, off64_t size, int type) -{ - char cmdline[256]; - int rc = -1; + ftr->kdf_type = KDF_SCRYPT; + get_device_scrypt_params(ftr); - if (type == EXT4_FS) { - snprintf(cmdline, sizeof(cmdline), "/system/bin/make_ext4fs -a /data -l %lld %s", - size * 512, crypto_blkdev); - SLOGI("Making empty filesystem with command %s\n", cmdline); - } else if (type== FAT_FS) { - snprintf(cmdline, sizeof(cmdline), "/system/bin/newfs_msdos -F 32 -O android -c 8 -s %lld %s", - size, crypto_blkdev); - SLOGI("Making empty filesystem with command %s\n", cmdline); - } else { - SLOGE("cryptfs_enable_wipe(): unknown filesystem type %d\n", type); - return -1; - } - - if (system(cmdline)) { - SLOGE("Error creating empty filesystem on %s\n", crypto_blkdev); - } else { - SLOGD("Successfully created empty filesystem on %s\n", crypto_blkdev); - rc = 0; + 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; } - - return rc; } -static inline int unix_read(int fd, void* buff, int len) -{ - int ret; - do { ret = read(fd, buff, len); } while (ret < 0 && errno == EINTR); - return ret; -} - -static inline int unix_write(int fd, const void* buff, int len) +static int cryptfs_enable_wipe(char *crypto_blkdev, off64_t size, int type) { - int ret; - do { ret = write(fd, buff, len); } while (ret < 0 && errno == EINTR); - return ret; + return -1; } #define CRYPT_INPLACE_BUFSIZE 4096 @@ -1035,12 +1468,12 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_ off64_t blocks_already_done, tot_numblocks; if ( (realfd = open(real_blkdev, O_RDONLY)) < 0) { - SLOGE("Error opening real_blkdev %s for inplace encrypt\n", real_blkdev); + printf("Error opening real_blkdev %s for inplace encrypt\n", real_blkdev); return -1; } if ( (cryptofd = open(crypto_blkdev, O_WRONLY)) < 0) { - SLOGE("Error opening crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); + printf("Error opening crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); close(realfd); return -1; } @@ -1055,7 +1488,7 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_ tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE; blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE; - SLOGE("Encrypting filesystem in place..."); + printf("Encrypting filesystem in place..."); one_pct = tot_numblocks / 100; cur_pct = 0; @@ -1070,11 +1503,11 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_ property_set("vold.encrypt_progress", buf); } if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { - SLOGE("Error reading real_blkdev %s for inplace encrypt\n", crypto_blkdev); + printf("Error reading real_blkdev %s for inplace encrypt\n", crypto_blkdev); goto errout; } if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) { - SLOGE("Error writing crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); + printf("Error writing crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); goto errout; } } @@ -1082,11 +1515,11 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_ /* Do any remaining sectors */ for (i=0; i<remainder; i++) { if (unix_read(realfd, buf, 512) <= 0) { - SLOGE("Error reading rival sectors from real_blkdev %s for inplace encrypt\n", crypto_blkdev); + printf("Error reading rival sectors from real_blkdev %s for inplace encrypt\n", crypto_blkdev); goto errout; } if (unix_write(cryptofd, buf, 512) <= 0) { - SLOGE("Error writing final sectors to crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); + printf("Error writing final sectors to crypto_blkdev %s for inplace encrypt\n", crypto_blkdev); goto errout; } } @@ -1114,39 +1547,170 @@ static inline int should_encrypt(struct volume_info *volume) int cryptfs_enable(char *howarg, char *passwd) { - // Code removed because it needs other parts of vold that aren't needed for decryption return -1; } int cryptfs_changepw(char *newpw) { struct crypt_mnt_ftr crypt_ftr; - unsigned char encrypted_master_key[KEY_LEN_BYTES], decrypted_master_key[KEY_LEN_BYTES]; - unsigned char salt[SALT_LEN]; - char real_blkdev[MAXPATHLEN]; + unsigned char decrypted_master_key[KEY_LEN_BYTES]; /* This is only allowed after we've successfully decrypted the master key */ if (! master_key_saved) { - SLOGE("Key not saved, aborting"); - return -1; - } - - fs_mgr_get_crypt_info(get_fstab_filename(), 0, real_blkdev, sizeof(real_blkdev)); - if (strlen(real_blkdev) == 0) { - SLOGE("Can't find real blkdev"); + printf("Key not saved, aborting"); return -1; } /* get key */ - if (get_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt)) { - SLOGE("Error getting crypt footer and key"); + if (get_crypt_ftr_and_key(&crypt_ftr)) { + printf("Error getting crypt footer and key"); return -1; } - encrypt_master_key(newpw, salt, saved_master_key, encrypted_master_key); + encrypt_master_key(newpw, crypt_ftr.salt, saved_master_key, crypt_ftr.master_key, &crypt_ftr); /* save the key */ - put_crypt_ftr_and_key(real_blkdev, &crypt_ftr, encrypted_master_key, salt); + 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 index 1c1bc1aea..162159eb0 100644 --- a/crypto/jb/cryptfs.h +++ b/crypto/jb/cryptfs.h @@ -15,22 +15,31 @@ */ /* 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. + * 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. + * 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 -#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. */ @@ -38,9 +47,18 @@ * 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 __le16 unsigned short int +#define __le8 unsigned char struct crypt_mnt_ftr { __le32 magic; /* See above */ @@ -56,6 +74,48 @@ struct crypt_mnt_ftr { 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 { @@ -67,12 +127,17 @@ struct volume_info { char crypto_blkdev[256]; char label[256]; }; -#define VOL_NONREMOVABLE 0x1 -#define VOL_ENCRYPTABLE 0x2 +#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_passwd(char *pw); int cryptfs_verify_passwd(char *newpw); @@ -83,6 +148,8 @@ extern "C" { 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/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..bdaeccf30 --- /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 bootable/recovery/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 bootable/recovery/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..b25e1c420 --- /dev/null +++ b/crypto/scrypt/tests/Android.mk @@ -0,0 +1,25 @@ +# Build the scrypt unit tests + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +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"; + } +} + +} |