mirror of https://github.com/vitalif/e2fsprogs
misc: add facilities to insert keys to e4crypt
This patch adds the following options to the e4crypt: -a keyring [-k keyref] [-p passphrase] [-b hexstring] -r keyring -k keyref Where '-a' allows to add a key to a 'keyring', where key is derived from user typed passphrase (if both '-p' and '-b' are omitted), or from 'passphrase' specified by '-p' option, or stored 'as-is' if option '-b' is used. Command '-r' provides means for deleting a key from a keyring. Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>crypto
parent
37da3a1b31
commit
8f8be9f412
|
@ -16,6 +16,7 @@
|
|||
#ifndef _LINUX_EXT2_FS_H
|
||||
#define _LINUX_EXT2_FS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ext2fs/ext2_types.h> /* Changed from linux/types.h */
|
||||
|
||||
/*
|
||||
|
@ -565,12 +566,39 @@ struct ext2_inode_large {
|
|||
/* Metadata checksum algorithms */
|
||||
#define EXT2_CRC32C_CHKSUM 1
|
||||
|
||||
/* Encryption algorithms */
|
||||
/* Encryption algorithms, key size and key reference len */
|
||||
#define EXT4_ENCRYPTION_MODE_INVALID 0
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_GCM 2
|
||||
#define EXT4_ENCRYPTION_MODE_AES_256_CBC 3
|
||||
|
||||
#define EXT4_AES_256_XTS_KEY_SIZE 64
|
||||
#define EXT4_AES_256_GCM_KEY_SIZE 32
|
||||
#define EXT4_AES_256_CBC_KEY_SIZE 32
|
||||
/* Used to wrap data encryption keys. */
|
||||
#define EXT4_AES_256_CTR_KEY_SIZE 32
|
||||
#define EXT4_MAX_KEY_SIZE 76
|
||||
|
||||
#define EXT4_KEYREF_LOGON_PREFIX "ext4-key:"
|
||||
#define EXT4_KEYREF_LOGON_PREFIX_LEN 9
|
||||
#define EXT4_KEYREF_DERIVED_LEN 16
|
||||
#define EXT4_KEYREF_DERIVED_TOTAL_LEN (EXT4_KEYREF_LOGON_PREFIX_LEN +\
|
||||
EXT4_KEYREF_DERIVED_LEN)
|
||||
#define EXT4_KEYREF_MIN_LEN 8
|
||||
|
||||
/* Password derivation constants */
|
||||
#define EXT4_DEFAULT_SALT_SIZE 8
|
||||
#define EXT4_DEFAULT_SALT "fd7ea91d4f9dc1b5"
|
||||
#define EXT4_MAX_PASSWORD_LENGTH 64
|
||||
#define EXT4_PBKDF2_ITERATIONS 0xFFFF
|
||||
|
||||
/* MUST be in sync with ext4_crypto.c in kernel. */
|
||||
struct ext4_encryption_key {
|
||||
uint32_t mode;
|
||||
char raw[EXT4_MAX_KEY_SIZE];
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure of the super block
|
||||
*/
|
||||
|
|
|
@ -34,6 +34,18 @@ copies temp file on exit over the original one, then extended attributes in
|
|||
encryption name index will not be copied, i.e., encryption will be disabled.
|
||||
That is why an explicit user consent is required to set policy over a file,
|
||||
in order to avoid confusion.
|
||||
.IP "-a keyring"
|
||||
This allows to add a key to a keyring. By default, user will be promted to
|
||||
enter a passphrase. This behaviour can be changed by '-p passphrase' or
|
||||
'-b hexstring' options. The first one allows to specify passphrase in the
|
||||
command line, while the latter allows to specify a key in hex form. Note, that
|
||||
by default the key reference (i.e., searchable string in the keyring) is
|
||||
produced by hashing (SHA512) the content of the key and truncating the output to
|
||||
8 bytes. User can use '-k keyref' to provide alternative key reference name for
|
||||
the key.
|
||||
.IP "-r keyring"
|
||||
This command allows to remove a key from a keyring. Note, '-r' command requires
|
||||
option '-k keyref' to be provided.
|
||||
.SH AUTHOR
|
||||
Written by Michael Halcrow <mhalcrow@google.com> and Ildar Muslukhov
|
||||
<muslukhovi@gmail.com>.
|
||||
|
|
853
misc/e4crypt.c
853
misc/e4crypt.c
|
@ -2,6 +2,7 @@
|
|||
* e4crypt.c - ext4 encryption management utility
|
||||
*
|
||||
* Copyright (c) 2014 Google, Inc.
|
||||
* SHA512 implementation if borrowed from libtomcrypt.
|
||||
*
|
||||
* Authors: Michael Halcrow <mhalcrow@google.com>,
|
||||
* Ildar Muslukhov <ildarm@google.com>
|
||||
|
@ -30,12 +31,56 @@
|
|||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <syslog.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#include "ext2fs/ext2_fs.h"
|
||||
|
||||
/* special process keyring shortcut IDs */
|
||||
#define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific
|
||||
keyring */
|
||||
#define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific
|
||||
keyring */
|
||||
#define KEY_SPEC_SESSION_KEYRING -3 /* - key ID for session-specific
|
||||
keyring */
|
||||
#define KEY_SPEC_USER_KEYRING -4 /* - key ID for UID-specific
|
||||
keyring */
|
||||
#define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session
|
||||
keyring */
|
||||
#define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific
|
||||
keyring */
|
||||
#define KEY_SPEC_REQKEY_AUTH_KEY -7 /* - key ID for assumed
|
||||
request_key auth key */
|
||||
|
||||
/* keyring keyctl commands */
|
||||
#define KEYCTL_UNLINK 9 /* unlink a key from a keyring */
|
||||
#define KEYCTL_SEARCH 10 /* search for a key in a keyring */
|
||||
|
||||
/* keyring serial number type */
|
||||
typedef int32_t key_serial_t;
|
||||
|
||||
/*
|
||||
* syscall wrappers
|
||||
*/
|
||||
key_serial_t add_key(const char *type,
|
||||
const char *description,
|
||||
const void *payload,
|
||||
size_t plen,
|
||||
key_serial_t ringid);
|
||||
long keyctl_search(key_serial_t ringid,
|
||||
const char *type,
|
||||
const char *description,
|
||||
key_serial_t
|
||||
destringid);
|
||||
long keyctl_unlink(key_serial_t id, key_serial_t ringid);
|
||||
|
||||
enum mode_flags {
|
||||
MODE_FLAG_NONE = 0,
|
||||
|
@ -43,8 +88,16 @@ enum mode_flags {
|
|||
MODE_FLAG_POLICYGET = 2,
|
||||
MODE_FLAG_POLICYDELETE = 4,
|
||||
MODE_FLAG_DANGER = 8,
|
||||
MODE_FLAG_INSERT_KEY = 16,
|
||||
MODE_FLAG_REMOVE_KEY = 32,
|
||||
};
|
||||
|
||||
#define POLICY_WORK (MODE_FLAG_POLICYSET| \
|
||||
MODE_FLAG_POLICYGET| \
|
||||
MODE_FLAG_POLICYDELETE)
|
||||
#define KEY_WORK (MODE_FLAG_INSERT_KEY| \
|
||||
MODE_FLAG_REMOVE_KEY)
|
||||
|
||||
static enum mode_flags mode_flags = MODE_FLAG_NONE;
|
||||
|
||||
#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
|
||||
|
@ -53,23 +106,47 @@ static enum mode_flags mode_flags = MODE_FLAG_NONE;
|
|||
#define EXT4_KEYREF_DELIMITER ((char)'.')
|
||||
#define EXT4_POLICY_DELIMITER ((char)'#')
|
||||
|
||||
#define SHA512_LENGTH 64
|
||||
static void hash_sha512(char *in, unsigned long in_size,
|
||||
char out[SHA512_LENGTH]);
|
||||
|
||||
#define MSG_USAGE \
|
||||
"Usage : e4crypt command [policy] [path1 ... pathN]\n"\
|
||||
"Usage : e4crypt command [object] [options] [path1 ... pathN]\n"\
|
||||
" Where command might be one of the:\n"\
|
||||
" -g Get policy value\n"\
|
||||
" -s policy Set policy\n"\
|
||||
" -x Delete policy\n"\
|
||||
" -a keyring Insert a key into the keyring, defaults to\n"\
|
||||
" interactive passphrase mode.\n"\
|
||||
" -r keyring Remove a key from the keyring (must use option '-k')\n"\
|
||||
"OPTIONS:\n"\
|
||||
" -b hexstring A modifier to '-a' command that inserts converts "\
|
||||
" provided hexstring into a byte array and uses it as a"\
|
||||
" key. It should be 32 bytes long at most (64 chars)."\
|
||||
" -p passphrase A modifier to '-a' command that provides a passphrase"\
|
||||
" that is used for key generation. Currently we use 65K"\
|
||||
" iterations in pbkdf2 to derive a key."\
|
||||
"NOTES:\n"\
|
||||
" -s command over a file requires '-W ignore-danger' command as well.\n"\
|
||||
" -g,-s and -x commands require at least one path.\n"\
|
||||
" Several paths can be provided, for which the same set of commands\n"\
|
||||
" will be executed.\n"
|
||||
" will be executed.\n"\
|
||||
" -a commands defaults to passphrase if '-b hex' is not provided.\n"\
|
||||
" User can override key reference (i.e., a searchable string for\n"\
|
||||
" keyrings) by providing '-k keyref' modifier, otherwise, a hash value\n"\
|
||||
" of the key, converted to a string and truncated to 16 bytes, will be\n"\
|
||||
" used as keyref.\n"
|
||||
|
||||
static int do_work(int argc,
|
||||
char *argv[],
|
||||
int path_start_index,
|
||||
char *policy,
|
||||
size_t policy_len);
|
||||
static int do_policy_work(int argc,
|
||||
char *argv[],
|
||||
int path_start_index,
|
||||
char *policy,
|
||||
size_t policy_len);
|
||||
|
||||
static int do_key_work(char *keyring,
|
||||
char *keyref,
|
||||
char *hexvalue,
|
||||
char *passphrase);
|
||||
|
||||
/*
|
||||
* main() - Ext4 crypto management tool.
|
||||
|
@ -82,16 +159,44 @@ int main(int argc, char *argv[])
|
|||
int opt;
|
||||
char *policy = NULL;
|
||||
size_t policy_len = 0;
|
||||
char *keyring = NULL;
|
||||
char *keyref = NULL;
|
||||
char *hexvalue = NULL;
|
||||
char *passphrase = NULL;
|
||||
|
||||
if (argc == 1)
|
||||
goto fail;
|
||||
|
||||
while ((opt = getopt(argc, argv, "gs:xW:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "a:b:gk:p:r:s:xW:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
/* Key insertion. */
|
||||
mode_flags |= MODE_FLAG_INSERT_KEY;
|
||||
keyring = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
/* Key insertion in as-is mode, user provides a binary
|
||||
* value of the key in hex format. */
|
||||
hexvalue = optarg;
|
||||
break;
|
||||
case 'g':
|
||||
/* Get the policy value. */
|
||||
mode_flags |= MODE_FLAG_POLICYGET;
|
||||
break;
|
||||
case 'k':
|
||||
/* Explicit key ref value - user provides us with an
|
||||
* explicit value for the key reference, hence we do not
|
||||
* do any ref derviation. */
|
||||
keyref = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
/* Key removal. */
|
||||
mode_flags |= MODE_FLAG_REMOVE_KEY;
|
||||
keyring = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
/* Passphrase. */
|
||||
passphrase = optarg;
|
||||
break;
|
||||
case 's':
|
||||
/* Set policy value.*/
|
||||
mode_flags |= MODE_FLAG_POLICYSET;
|
||||
|
@ -119,8 +224,10 @@ int main(int argc, char *argv[])
|
|||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return do_work(argc, argv, optind, policy, policy_len);
|
||||
if (mode_flags & POLICY_WORK)
|
||||
return do_policy_work(argc, argv, optind, policy, policy_len);
|
||||
else if (mode_flags & KEY_WORK)
|
||||
return do_key_work(keyring, keyref, hexvalue, passphrase);
|
||||
fail:
|
||||
printf(MSG_USAGE);
|
||||
return 1;
|
||||
|
@ -361,12 +468,13 @@ static int do_policy_delete(int argc, char *argv[], int path_start_index)
|
|||
}
|
||||
}
|
||||
|
||||
/* do_work() - The main selector of what we actually do. */
|
||||
static int do_work(int argc,
|
||||
char *argv[],
|
||||
int path_start_index,
|
||||
char *policy,
|
||||
size_t policy_len)
|
||||
/* do_policy_work() - Policy work selector of what we actually do with a
|
||||
* policy. */
|
||||
static int do_policy_work(int argc,
|
||||
char *argv[],
|
||||
int path_start_index,
|
||||
char *policy,
|
||||
size_t policy_len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -375,8 +483,717 @@ static int do_work(int argc,
|
|||
} else if (mode_flags & MODE_FLAG_POLICYSET) {
|
||||
ret = do_policy_set(argc, argv, path_start_index,
|
||||
policy, policy_len);
|
||||
} else if (mode_flags & MODE_FLAG_POLICYDELETE) {
|
||||
} else if (mode_flags & MODE_FLAG_POLICYDELETE) {
|
||||
ret = do_policy_delete(argc, argv, path_start_index);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function expect keyring value to be valid. */
|
||||
static int get_keyring_id(char *keyring)
|
||||
{
|
||||
int len = strlen(keyring);
|
||||
|
||||
if (strncmp(keyring, "@t", len) == 0)
|
||||
return KEY_SPEC_THREAD_KEYRING;
|
||||
if (strncmp(keyring, "@p", len) == 0)
|
||||
return KEY_SPEC_PROCESS_KEYRING;
|
||||
if (strncmp(keyring, "@s", len) == 0)
|
||||
return KEY_SPEC_SESSION_KEYRING;
|
||||
if (strncmp(keyring, "@u", len) == 0)
|
||||
return KEY_SPEC_USER_KEYRING;
|
||||
if (strncmp(keyring, "@g", len) == 0)
|
||||
return KEY_SPEC_GROUP_KEYRING;
|
||||
if (strncmp(keyring, "@us", len) == 0)
|
||||
return KEY_SPEC_USER_SESSION_KEYRING;
|
||||
}
|
||||
static int is_keyring_valid(char *keyring)
|
||||
{
|
||||
int len = strlen(keyring);
|
||||
|
||||
if (strncmp(keyring, "@t", len) == 0 ||
|
||||
strncmp(keyring, "@p", len) == 0 ||
|
||||
strncmp(keyring, "@s", len) == 0 ||
|
||||
strncmp(keyring, "@u", len) == 0 ||
|
||||
strncmp(keyring, "@g", len) == 0 ||
|
||||
strncmp(keyring, "@us", len) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void do_hash(char *src, int src_size, char *dst)
|
||||
{
|
||||
hash_sha512(src, src_size, dst);
|
||||
}
|
||||
|
||||
static int hex2byte(char *hexvalue, char *keyblob, size_t *keyblob_size)
|
||||
{
|
||||
char *hexchars = "0123456789abcdef";
|
||||
size_t hexchars_len = strlen(hexchars);
|
||||
int i, max;
|
||||
char *h,*l;
|
||||
|
||||
if (keyblob_size)
|
||||
*keyblob_size = 0;
|
||||
for (i = 0; i < strlen(hexvalue); i+=2) {
|
||||
h = memchr(hexchars, hexvalue[i], hexchars_len);
|
||||
l = memchr(hexchars, hexvalue[i + 1], hexchars_len);
|
||||
if (h == NULL || l == NULL) {
|
||||
*keyblob_size = 0;
|
||||
/* Well, its an invalid hex, exit.*/
|
||||
printf("Ivalid hex value (\"%s\").\n", hexvalue);
|
||||
return -EINVAL;
|
||||
}
|
||||
keyblob[i >> 1] = (char)(h - hexchars) << 4 +
|
||||
(char)(l - hexchars);
|
||||
if (keyblob_size)
|
||||
*keyblob_size += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void derive_keyref(char *keyblob, size_t keyblob_size,
|
||||
char *keyref, size_t keyref_size)
|
||||
{
|
||||
char *hexchars = "0123456789abcdef";
|
||||
char buf[SHA512_LENGTH];
|
||||
int i;
|
||||
|
||||
do_hash(keyblob, keyblob_size, buf);
|
||||
|
||||
/* Convert digest to hex string */
|
||||
for (i = 0; i < (keyref_size - 1) >> 1; i++) {
|
||||
keyref[i << 1] = hexchars[(buf[i] & 0xf0) >> 4];
|
||||
keyref[(i << 1) + 1] =
|
||||
hexchars[buf[i] & 0x0f];
|
||||
}
|
||||
keyref[keyref_size - 1] = '\0';
|
||||
}
|
||||
|
||||
static int key_insert_into_keyring(char *keyring,
|
||||
char *keyblob,
|
||||
int keyblob_size,
|
||||
char *keyref)
|
||||
{
|
||||
int ret;
|
||||
int keyring_id = get_keyring_id(keyring);
|
||||
struct ext4_encryption_key key;
|
||||
|
||||
ret = (int)keyctl_search(keyring_id,
|
||||
"logon",
|
||||
keyref,
|
||||
0);
|
||||
if (ret != -1) {
|
||||
printf("Key with that id already exist in the destination"
|
||||
" keyring.\n");
|
||||
ret = 0;
|
||||
goto out;
|
||||
} else if ((ret == -1) && (errno != ENOKEY)) {
|
||||
int errnum = errno;
|
||||
syslog(LOG_ERR, "keyctl_search failed: %m errno=[%d]\n",
|
||||
errnum);
|
||||
ret = (errnum < 0) ? errnum : errnum * -1;
|
||||
if (ret == -EINVAL)
|
||||
printf("Keyring %s is not available.\n", keyring);
|
||||
goto out;
|
||||
}
|
||||
key.mode = EXT4_ENCRYPTION_MODE_INVALID;
|
||||
/* Copy over whole array to copy zero bytes at the end too. */
|
||||
memcpy(key.raw, keyblob, EXT4_MAX_KEY_SIZE);
|
||||
key.size = keyblob_size;
|
||||
ret = add_key("logon", keyref, (void *)&key,
|
||||
sizeof(struct ext4_encryption_key),
|
||||
keyring_id);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
syslog(LOG_ERR, "Error adding key with keyref [%s]; ret = [%d] "
|
||||
"\"%m\"\n", keyref, ret);
|
||||
if (ret == -EDQUOT)
|
||||
syslog(LOG_WARNING, "Error adding key to keyring -"
|
||||
" keyring is full\n");
|
||||
printf("Failed to add key to keyring (%d).\n", ret);
|
||||
goto out;
|
||||
} else {
|
||||
printf("Key was successfuly inserted into %s keyring (ref=%s,"
|
||||
" size=%d, serial=%d)\n", keyring, keyref,
|
||||
keyblob_size, ret);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int key_remove_from_keyring(char *keyring, char *keyref)
|
||||
{
|
||||
int ret;
|
||||
int keyring_id = get_keyring_id(keyring);
|
||||
|
||||
ret = (int)keyctl_search(keyring_id,
|
||||
"logon",
|
||||
keyref,
|
||||
0);
|
||||
|
||||
if (ret < 0) {
|
||||
ret = errno;
|
||||
syslog(LOG_ERR, "Failed to find key with ref [%s]: %m\n",
|
||||
keyref);
|
||||
goto out;
|
||||
}
|
||||
ret = keyctl_unlink(ret, keyring_id);
|
||||
if (ret < 0) {
|
||||
ret = errno;
|
||||
syslog(LOG_ERR, "Failed to unlink key with ref [%s]: %s\n",
|
||||
keyref, strerror(ret));
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
printf("Key [%s] was deleted from %s keyring.\n", keyref, keyring);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int key_insert_binary_blob(char *keyring,
|
||||
char *keyref,
|
||||
char *keyblob,
|
||||
size_t keyblob_size)
|
||||
{
|
||||
int ret = 0;
|
||||
char keyref_derived[EXT4_KEYREF_DERIVED_TOTAL_LEN + 1];
|
||||
char *keyref_provided = NULL;
|
||||
char *keyreference = keyref;
|
||||
|
||||
/* Now obtain the keyref, i.e., a searchable string that identifies the
|
||||
* key we are about to insert into keyring. */
|
||||
if (!keyref) {
|
||||
strncpy(keyref_derived,
|
||||
EXT4_KEYREF_LOGON_PREFIX,
|
||||
EXT4_KEYREF_LOGON_PREFIX_LEN);
|
||||
derive_keyref(keyblob, keyblob_size,
|
||||
&keyref_derived[EXT4_KEYREF_LOGON_PREFIX_LEN],
|
||||
EXT4_KEYREF_DERIVED_LEN + 1);
|
||||
keyreference = keyref_derived;
|
||||
} else {
|
||||
/* Check min length. Nothing serious, just to prevent users from
|
||||
* using too short key refs. */
|
||||
if (strlen(keyref) < EXT4_KEYREF_MIN_LEN) {
|
||||
printf("Keyref is too short, at least %d chars are"
|
||||
" required.\n", EXT4_KEYREF_MIN_LEN);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
keyref_provided = malloc(strlen(keyref) +
|
||||
EXT4_KEYREF_LOGON_PREFIX_LEN + 1);
|
||||
if (!keyref_provided) {
|
||||
printf("Unable to allocate memory for key reference.\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* copy prefix */
|
||||
strncpy(keyref_provided,
|
||||
EXT4_KEYREF_LOGON_PREFIX,
|
||||
EXT4_KEYREF_LOGON_PREFIX_LEN);
|
||||
strncpy(&keyref_provided[EXT4_KEYREF_LOGON_PREFIX_LEN],
|
||||
keyref,
|
||||
strlen(keyref));
|
||||
keyref_provided[EXT4_KEYREF_LOGON_PREFIX_LEN + strlen(keyref)] =
|
||||
'\0';
|
||||
keyreference = keyref_provided;
|
||||
}
|
||||
|
||||
ret = key_insert_into_keyring(keyring,
|
||||
keyblob, keyblob_size,
|
||||
keyreference);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int key_insert_hexstring(char *keyring,
|
||||
char *keyref,
|
||||
char *hexvalue)
|
||||
{
|
||||
char keyblob[EXT4_MAX_KEY_SIZE];
|
||||
size_t keyblob_size = 0;
|
||||
int ret = 0;
|
||||
|
||||
if ((strlen(hexvalue) >> 1) > EXT4_MAX_KEY_SIZE) {
|
||||
printf("Hex value length is too long. Maximum supported key"
|
||||
" length is %d.\n", EXT4_MAX_KEY_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(keyblob, 0, EXT4_MAX_KEY_SIZE);
|
||||
ret = hex2byte(hexvalue, keyblob, &keyblob_size);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* All keys inserted from user-space are not used directly to encrypt
|
||||
* data. They are only used to wrap Data Encryption Keys with CTR mode.
|
||||
* Which means, we need at least 256 bit key. */
|
||||
if (keyblob_size < EXT4_AES_256_CTR_KEY_SIZE) {
|
||||
printf("The length of the key is smaller than minimal requred"
|
||||
" (%d < %d).Consider using passphrase insead.\n",
|
||||
(int)keyblob_size,
|
||||
EXT4_AES_256_CTR_KEY_SIZE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = key_insert_binary_blob(keyring, keyref, keyblob, keyblob_size);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int disable_echo(struct termios *saved_settings)
|
||||
{
|
||||
struct termios current_settings;
|
||||
int rc = 0;
|
||||
|
||||
rc = tcgetattr(0, ¤t_settings);
|
||||
if (rc)
|
||||
return rc;
|
||||
*saved_settings =
|
||||
current_settings;
|
||||
current_settings.c_lflag &= ~ECHO;
|
||||
rc = tcsetattr(0, TCSANOW, ¤t_settings);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int enable_echo(struct termios *saved_settings)
|
||||
{
|
||||
return tcsetattr(0, TCSANOW, saved_settings);
|
||||
}
|
||||
|
||||
/* Note, we are producing a 32 bit key at most, since the key is needed for
|
||||
* wrapped key packet protection in CTR mode. */
|
||||
static void pbkdf2_sha512(char *passphrase,
|
||||
char *salt,
|
||||
int count,
|
||||
char derived_key[EXT4_AES_256_CTR_KEY_SIZE])
|
||||
{
|
||||
char buf[SHA512_LENGTH + EXT4_MAX_PASSWORD_LENGTH];
|
||||
char tempbuf[SHA512_LENGTH], final[SHA512_LENGTH];
|
||||
char saltbuf[EXT4_DEFAULT_SALT_SIZE + EXT4_MAX_PASSWORD_LENGTH];
|
||||
int buf_len = SHA512_LENGTH + strlen(passphrase);
|
||||
int saltbuf_len = EXT4_DEFAULT_SALT_SIZE + strlen(passphrase);
|
||||
int i, j;
|
||||
uint32_t *final_u32 = (uint32_t *)final;
|
||||
uint32_t *temp_u32 = (uint32_t *)tempbuf;
|
||||
|
||||
memset(final, 0, SHA512_LENGTH);
|
||||
/* Prepare intermediate blob .*/
|
||||
memcpy(&buf[SHA512_LENGTH], passphrase, strlen(passphrase));
|
||||
/* Init initial blob. */
|
||||
hex2byte(salt, saltbuf, NULL);
|
||||
memcpy(&saltbuf[EXT4_DEFAULT_SALT_SIZE], passphrase,
|
||||
strlen(passphrase));
|
||||
for (i = 0; i < count; i++) {
|
||||
if (i == 0) {
|
||||
do_hash(saltbuf, saltbuf_len, tempbuf);
|
||||
} else {
|
||||
do_hash(buf, buf_len, tempbuf);
|
||||
}
|
||||
for (j = 0; j < (SHA512_LENGTH >> 2); j++) {
|
||||
final_u32[j] = final_u32[j] ^ temp_u32[j];
|
||||
}
|
||||
memcpy(buf, tempbuf, SHA512_LENGTH);
|
||||
}
|
||||
|
||||
/* Copy required bytes. */
|
||||
memcpy(derived_key, final, EXT4_AES_256_CTR_KEY_SIZE);
|
||||
}
|
||||
|
||||
static int key_insert_passphrase(char *keyring,
|
||||
char *keyref,
|
||||
char *_passphrase)
|
||||
{
|
||||
int ret = 0;
|
||||
char *p;
|
||||
char keyblob[EXT4_AES_256_CTR_KEY_SIZE];
|
||||
char *passphrase = NULL;
|
||||
size_t salt_byte_size;
|
||||
struct termios current_settings;
|
||||
|
||||
if (!_passphrase) {
|
||||
passphrase = malloc(EXT4_MAX_PASSWORD_LENGTH + 2);
|
||||
if (!passphrase) {
|
||||
ret = -ENOMEM;
|
||||
printf("Unable to allocate memory for password.\n");
|
||||
goto out;
|
||||
}
|
||||
printf("Passphrase:");
|
||||
disable_echo(¤t_settings);
|
||||
if (fgets(passphrase,
|
||||
EXT4_MAX_PASSWORD_LENGTH + 2,
|
||||
stdin) == NULL) {
|
||||
enable_echo(¤t_settings);
|
||||
printf("\n");
|
||||
ret = -ENOKEY;
|
||||
goto out_free_passphrase;
|
||||
}
|
||||
enable_echo(¤t_settings);
|
||||
p = strrchr(passphrase, '\n');
|
||||
if (p) {
|
||||
*p = '\0';
|
||||
} else {
|
||||
printf("Cannot finde cr\n");
|
||||
}
|
||||
printf("\n");
|
||||
if (strlen(passphrase) > EXT4_MAX_PASSWORD_LENGTH) {
|
||||
fprintf(stderr,"Passphrase is too long. Use at most %u "
|
||||
"characters long passphrase.\n",
|
||||
EXT4_MAX_PASSWORD_LENGTH);
|
||||
ret = -EINVAL;
|
||||
goto out_free_passphrase;
|
||||
}
|
||||
} else {
|
||||
passphrase = _passphrase;
|
||||
}
|
||||
|
||||
/* TODO(ildar): This is the place where we should override default
|
||||
* salt value. */
|
||||
pbkdf2_sha512(passphrase, EXT4_DEFAULT_SALT,
|
||||
EXT4_PBKDF2_ITERATIONS, keyblob);
|
||||
ret = key_insert_binary_blob(keyring, keyref,
|
||||
keyblob,
|
||||
EXT4_AES_256_CTR_KEY_SIZE);
|
||||
out_free_passphrase:
|
||||
if (!_passphrase)
|
||||
free(passphrase);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int do_key_insert(char *keyring,
|
||||
char *keyref,
|
||||
char *hexvalue,
|
||||
char *passphrase)
|
||||
{
|
||||
/* Check if we should do "AS-IS" insert or based on passphrase */
|
||||
if (hexvalue) {
|
||||
/* As-is approach, that is, user specifies hex representation of
|
||||
* the key. */
|
||||
return key_insert_hexstring(keyring, keyref, hexvalue);
|
||||
} else {
|
||||
/* Passphrase approach, note, if '-p' is not used user will be
|
||||
* promted to type in a passphrase. */
|
||||
return key_insert_passphrase(keyring, keyref, passphrase);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int do_key_remove(char *keyring,
|
||||
char *keyref)
|
||||
{
|
||||
if (keyref) {
|
||||
return key_remove_from_keyring(keyring, keyref);
|
||||
} else {
|
||||
printf("Cannot delete a key without key reference.\n");
|
||||
return -ENOKEY;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int do_key_work(char *keyring,
|
||||
char *keyref,
|
||||
char *hexvalue,
|
||||
char *passphrase)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Validate keyring*/
|
||||
if (!is_keyring_valid(keyring)) {
|
||||
printf("Invalid keyring name (%s). Consult keyctl manual for proper"
|
||||
" names.\n", keyring);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mode_flags & MODE_FLAG_INSERT_KEY) {
|
||||
ret = do_key_insert(keyring, keyref, hexvalue, passphrase);
|
||||
} else if (mode_flags & MODE_FLAG_REMOVE_KEY) {
|
||||
ret = do_key_remove(keyring, keyref);
|
||||
if (ret)
|
||||
printf("Failed to remove key %s from %s.\n",
|
||||
keyref, keyring);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
long keyctl(int cmd, ...)
|
||||
{
|
||||
va_list va;
|
||||
unsigned long arg2, arg3, arg4, arg5;
|
||||
|
||||
va_start(va, cmd);
|
||||
arg2 = va_arg(va, unsigned long);
|
||||
arg3 = va_arg(va, unsigned long);
|
||||
arg4 = va_arg(va, unsigned long);
|
||||
arg5 = va_arg(va, unsigned long);
|
||||
va_end(va);
|
||||
return syscall(__NR_keyctl,
|
||||
cmd, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
key_serial_t add_key(const char *type,
|
||||
const char *description,
|
||||
const void *payload,
|
||||
size_t plen,
|
||||
key_serial_t ringid)
|
||||
{
|
||||
return syscall(__NR_add_key,
|
||||
type, description, payload, plen,
|
||||
ringid);
|
||||
}
|
||||
|
||||
long keyctl_search(key_serial_t ringid,
|
||||
const char *type,
|
||||
const char *description,
|
||||
key_serial_t
|
||||
destringid)
|
||||
{
|
||||
return keyctl(KEYCTL_SEARCH, ringid, type, description,
|
||||
destringid);
|
||||
}
|
||||
|
||||
long keyctl_unlink(key_serial_t id, key_serial_t ringid)
|
||||
{
|
||||
return keyctl(KEYCTL_UNLINK, id, ringid);
|
||||
}
|
||||
|
||||
/* SHA512 Implementation (copied from libtomcrypt) */
|
||||
/* the K array */
|
||||
#define CONST64(n) n
|
||||
typedef unsigned long long ulong64;
|
||||
static const ulong64 K[80] = {
|
||||
CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
|
||||
CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
|
||||
CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
|
||||
CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
|
||||
CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
|
||||
CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
|
||||
CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
|
||||
CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
|
||||
CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
|
||||
CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
|
||||
CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
|
||||
CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
|
||||
CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
|
||||
CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
|
||||
CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
|
||||
CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
|
||||
CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
|
||||
CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
|
||||
CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
|
||||
CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
|
||||
CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
|
||||
CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
|
||||
CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
|
||||
CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
|
||||
CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
|
||||
CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
|
||||
CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
|
||||
CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
|
||||
CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
|
||||
CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
|
||||
CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
|
||||
CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
|
||||
CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
|
||||
CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
|
||||
CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
|
||||
CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
|
||||
CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
|
||||
CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
|
||||
CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
|
||||
CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
|
||||
};
|
||||
#define Ch(x,y,z) (z ^ (x & (y ^ z)))
|
||||
#define Maj(x,y,z) (((x | y) & z) | (x & y))
|
||||
#define S(x, n) ROR64c(x, n)
|
||||
#define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)n))
|
||||
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
|
||||
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
|
||||
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
|
||||
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
|
||||
#define RND(a,b,c,d,e,f,g,h,i)\
|
||||
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i];\
|
||||
t1 = Sigma0(a) + Maj(a, b, c);\
|
||||
d += t0;\
|
||||
h = t0 + t1;
|
||||
#define STORE64H(x, y) \
|
||||
do { \
|
||||
(y)[0] = (unsigned char)(((x)>>56)&255);\
|
||||
(y)[1] = (unsigned char)(((x)>>48)&255);\
|
||||
(y)[2] = (unsigned char)(((x)>>40)&255);\
|
||||
(y)[3] = (unsigned char)(((x)>>32)&255);\
|
||||
(y)[4] = (unsigned char)(((x)>>24)&255);\
|
||||
(y)[5] = (unsigned char)(((x)>>16)&255);\
|
||||
(y)[6] = (unsigned char)(((x)>>8)&255);\
|
||||
(y)[7] = (unsigned char)((x)&255); } while(0)
|
||||
|
||||
#define LOAD64H(x, y)\
|
||||
do {x = \
|
||||
(((ulong64)((y)[0] & 255)) << 56) |\
|
||||
(((ulong64)((y)[1] & 255)) << 48) |\
|
||||
(((ulong64)((y)[2] & 255)) << 40) |\
|
||||
(((ulong64)((y)[3] & 255)) << 32) |\
|
||||
(((ulong64)((y)[4] & 255)) << 24) |\
|
||||
(((ulong64)((y)[5] & 255)) << 16) |\
|
||||
(((ulong64)((y)[6] & 255)) << 8) |\
|
||||
(((ulong64)((y)[7] & 255)));\
|
||||
} while(0)
|
||||
|
||||
#define ROR64c(word,i) ({ \
|
||||
ulong64 __ROR64c_tmp = word; \
|
||||
__asm__ ("rorq %2, %0" : \
|
||||
"=r" (__ROR64c_tmp) : \
|
||||
"0" (__ROR64c_tmp), \
|
||||
"J" (i)); \
|
||||
__ROR64c_tmp; })
|
||||
|
||||
struct sha512_state {
|
||||
ulong64 length, state[8];
|
||||
unsigned long curlen;
|
||||
unsigned char buf[128];
|
||||
};
|
||||
|
||||
/* This is a highly simplified version from libtomcrypt */
|
||||
struct hash_state {
|
||||
struct sha512_state sha512;
|
||||
};
|
||||
|
||||
static void sha512_compress(struct hash_state * md, const unsigned char *buf)
|
||||
{
|
||||
ulong64 S[8], W[80], t0, t1;
|
||||
int i;
|
||||
|
||||
/* copy state into S */
|
||||
for (i = 0; i < 8; i++) {
|
||||
S[i] = md->sha512.state[i];
|
||||
}
|
||||
|
||||
/* copy the state into 1024-bits into W[0..15] */
|
||||
for (i = 0; i < 16; i++) {
|
||||
LOAD64H(W[i], buf + (8*i));
|
||||
}
|
||||
|
||||
/* fill W[16..79] */
|
||||
for (i = 16; i < 80; i++) {
|
||||
W[i] = Gamma1(W[i - 2]) + W[i - 7] +
|
||||
Gamma0(W[i - 15]) + W[i - 16];
|
||||
}
|
||||
|
||||
for (i = 0; i < 80; i += 8) {
|
||||
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
|
||||
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
|
||||
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
|
||||
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
|
||||
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
|
||||
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
|
||||
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
|
||||
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
|
||||
}
|
||||
|
||||
/* feedback */
|
||||
for (i = 0; i < 8; i++) {
|
||||
md->sha512.state[i] = md->sha512.state[i] + S[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void sha512_init(struct hash_state * md)
|
||||
{
|
||||
md->sha512.curlen = 0;
|
||||
md->sha512.length = 0;
|
||||
md->sha512.state[0] = CONST64(0x6a09e667f3bcc908);
|
||||
md->sha512.state[1] = CONST64(0xbb67ae8584caa73b);
|
||||
md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b);
|
||||
md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1);
|
||||
md->sha512.state[4] = CONST64(0x510e527fade682d1);
|
||||
md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f);
|
||||
md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b);
|
||||
md->sha512.state[7] = CONST64(0x5be0cd19137e2179);
|
||||
}
|
||||
|
||||
static void sha512_done(struct hash_state * md, unsigned char *out)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* increase the length of the message */
|
||||
md->sha512.length += md->sha512.curlen * CONST64(8);
|
||||
|
||||
/* append the '1' bit */
|
||||
md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80;
|
||||
|
||||
/* if the length is currently above 112 bytes we append zeros then
|
||||
* compress. Then we can fall back to padding zeros and length encoding
|
||||
* like normal. */
|
||||
if (md->sha512.curlen > 112) {
|
||||
while (md->sha512.curlen < 128) {
|
||||
md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
|
||||
}
|
||||
sha512_compress(md, md->sha512.buf);
|
||||
md->sha512.curlen = 0;
|
||||
}
|
||||
|
||||
/* pad upto 120 bytes of zeroes note: that from 112 to 120 is the 64 MSB
|
||||
* of the length. We assume that you won't hash > 2^64 bits of data. */
|
||||
while (md->sha512.curlen < 120) {
|
||||
md->sha512.buf[md->sha512.curlen++] = (unsigned char)0;
|
||||
}
|
||||
|
||||
/* store length */
|
||||
STORE64H(md->sha512.length, md->sha512.buf + 120);
|
||||
sha512_compress(md, md->sha512.buf);
|
||||
|
||||
/* copy output */
|
||||
for (i = 0; i < 8; i++) {
|
||||
STORE64H(md->sha512.state[i], out+(8 * i));
|
||||
}
|
||||
}
|
||||
|
||||
#define MIN(x, y) ( ((x)<(y))?(x):(y) )
|
||||
#define SHA512_BLOCKSIZE 512
|
||||
static void sha512_process(struct hash_state * md,
|
||||
unsigned char *in,
|
||||
unsigned long inlen)
|
||||
{
|
||||
unsigned long n;
|
||||
|
||||
while (inlen > 0) {
|
||||
if (md->sha512.curlen == 0 && inlen >= SHA512_BLOCKSIZE) {
|
||||
sha512_compress(md, in);
|
||||
md->sha512.length += SHA512_BLOCKSIZE * 8;
|
||||
in += SHA512_BLOCKSIZE;
|
||||
inlen -= SHA512_BLOCKSIZE;
|
||||
} else {
|
||||
n = MIN(inlen, (SHA512_BLOCKSIZE - md->sha512.curlen));
|
||||
memcpy(md->sha512.buf + md->sha512.curlen,
|
||||
in, (size_t)n);
|
||||
md->sha512.curlen += n;
|
||||
in += n;
|
||||
inlen -= n;
|
||||
if (md->sha512.curlen == SHA512_BLOCKSIZE) {
|
||||
sha512_compress(md, md->sha512.buf);
|
||||
md->sha512.length += SHA512_BLOCKSIZE * 8;
|
||||
md->sha512.curlen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hash_sha512(char *in, unsigned long in_size,
|
||||
char out[SHA512_LENGTH])
|
||||
{
|
||||
struct hash_state md;
|
||||
|
||||
sha512_init(&md);
|
||||
sha512_process(&md, in, in_size);
|
||||
sha512_done(&md, out);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue