Паранойя не лечится! Но и не преследуется по закону. Поэтому в Linux Kernel 4.1 добавлена поддержка шифрования файловой системы ext4 на уровне отдельных файлов и директорий. Зашифровать можно только пустую директорию. Все файлы, которые будут созданы в такой директории, также будут зашифрованы. Шифруются только имена файлов и содержимое, метаданные не шифруются, inline data (когда данные файла, не превышающие по размеру 60 байт, хранятся в айноде) в файлах не поддерживается. Поскольку расшифровка содержимого файла выполняется непосредственно в памяти, шифрование доступно только в том случае, когда размер кластера совпадает с PAGE_SIZE, т.е. равен 4К.
# mkfs.ext4 -O encrypt /dev/xxx
# tune2fs -O encrypt /dev/xxx
# mount /dev/xxx /mnt/xxx
$ e4crypt add_key
Enter passphrase (echo disabled):
Added key with descriptor [8e679e4449bb9235]
$ keyctl show
Session Keyring
771961813 --alswrv 1000 65534 keyring: _uid_ses.1000
771026675 --alswrv 1000 65534 \_ keyring: _uid.1000
803843970 --alsw-v 1000 1000 \_ logon: ext4:8e679e4449bb9235
$ mkdir /mnt/xxx/encrypted_folder
$ e4crypt set_policy 8e679e4449bb9235 /mnt/xxx/encrypted_folder/
Key with descriptor [8e679e4449bb9235] applied to /mnt/xxx/encrypted_folder/.
$ e4crypt get_policy /mnt/xxx/encrypted_folder/
/mnt/xxx/encrypted_folder/: 8e679e4449bb9235
$ e4crypt add_key
Enter passphrase (echo disabled):
Added key with descriptor [9dafe822ae6e7994]
$ e4crypt set_policy 9dafe822ae6e7994 /mnt/xxx/encrypted_folder/
Error [Invalid argument] setting policy.
The key descriptor [9dafe822ae6e7994] may not match the existing encryption context for directory [/mnt/xxx/encrypted_folder/].
$ rm -rf /mnt/xxx/encrypted_folder/
$ ll /mnt/xxx
total 24
drwxr-xr-x 3 user user 4096 Apr 21 15:14 ./
drwxr-xr-x 4 root root 4096 Mar 29 15:30 ../
drwx------ 2 root root 16384 Apr 17 12:41 lost+found/
$
$ echo "My secret file content" > /mnt/xxx/encrypted_folder/my_secrets.txt
$ cat /mnt/xxx/encrypted_folder/my_secrets.txt
My secret file content
$ ll /mnt/xxx/encrypted_folder/
total 12
drwxr-xr-x 2 user user 4096 Apr 20 14:25 ./
drwxr-xr-x 5 user user 4096 Apr 20 14:15 ../
-rw-r--r-- 1 user user 23 Apr 20 14:26 my_secrets.txt
$ keyctl revoke 803843970
$ keyctl show
Session Keyring
771961813 --alswrv 1000 65534 keyring: _uid_ses.1000
771026675 --alswrv 1000 65534 \_ keyring: _uid.1000
803843970: key inaccessible (Key has been revoked)
$ ll /mnt/xxx/encrypted_folder/
total 12
drwxr-xr-x 2 user user 4096 Apr 20 14:25 ./
drwxr-xr-x 5 user user 4096 Apr 20 14:15 ../
-rw-r--r-- 1 user user 23 Apr 20 14:26 BhqTNRNHDBwpa9S1qCaXwC
$ cat /mnt/xxx/encrypted_folder/BhqTNRNHDBwpa9S1qCaXwC
cat: /mnt/xxx/encrypted_folder/BhqTNRNHDBwpa9S1qCaXwC: Required key not available
$ keyctl show
Session Keyring
771961813 --alswrv 1000 65534 keyring: _uid_ses.1000
771026675 --alswrv 1000 65534 \_ keyring: _uid.1000
$ e4crypt get_policy /mnt/xxx/encrypted_folder/
/mnt/xxx/encrypted_folder/: 8e679e4449bb9235
$ ll /mnt/xxx/encrypted_folder/
total 12
drwxr-xr-x 2 user user 4096 Apr 20 14:25 ./
drwxr-xr-x 5 user user 4096 Apr 20 14:15 ../
-rw-r--r-- 1 user user 23 Apr 20 14:26 my_secrets.txt
$ cat /mnt/xxx/encrypted_folder/my_secrets.txt
My secret file content
# umount /dev/xxx
# mount /dev/xxx /mnt/xxx
$ ll /mnt/xxx/encrypted_folder/
total 12
drwxr-xr-x 2 user user 4096 Apr 20 14:25 ./
drwxr-xr-x 5 user user 4096 Apr 20 14:15 ../
-rw-r--r-- 1 user user 23 Apr 20 14:26 BhqTNRNHDBwpa9S1qCaXwC
$ stat /mnt/xxx/encrypted_folder/
File: /mnt/xxx/encrypted_folder/
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: 811h/2065d Inode: 14 Links: 2
# dd if=/dev/xxx of=gdt bs=4096 count=1 skip=1
# dd if=/dev/xxx of=itable bs=4096 count=1 skip=1060
# dd if=/dev/xxx of=dirdata bs=4096 count=1 skip=1026
# dd if=/dev/xxx of=filedata bs=4096 count=1 skip=43520
typedef enum { ENCRYPT, DECRYPT } cipher_mode;
static int do_blkcrypt(const u8* cipher, const u8* key, u32 key_len,
void* iv, void* dst, void* src, size_t src_len, cipher_mode mode)
{
int res;
struct crypto_blkcipher* blk;
struct blkcipher_desc desc;
struct scatterlist sg_src, sg_dst;
blk = crypto_alloc_blkcipher(cipher, 0, 0);
if (IS_ERR(blk))
{
printk(KERN_WARNING "Failed to initialize blkcipher mode %s\n", cipher);
return PTR_ERR(blk);
}
res = crypto_blkcipher_setkey(blk, key, key_len);
if (res)
{
printk(KERN_WARNING "Failed to set key. len=%#x\n", key_len);
crypto_free_blkcipher(blk);
return res;
}
crypto_blkcipher_set_iv(blk, iv, 16);
sg_init_one(&sg_src, src, src_len);
sg_init_one(&sg_dst, dst, src_len);
desc.tfm = blk;
desc.flags = 0;
if (mode == ENCRYPT)
res = crypto_blkcipher_encrypt(&desc, &sg_dst, &sg_src, src_len);
else
res = crypto_blkcipher_decrypt(&desc, &sg_dst, &sg_src, src_len);
crypto_free_blkcipher(blk);
return res;
}
struct tcrypt_result {
struct completion completion;
int err;
};
static void crypt_complete_cb(struct crypto_async_request* req, int error)
{
struct tcrypt_result* res = req->data;
if (error == -EINPROGRESS)
return;
res->err = error;
complete(&res->completion);
}
static int do_skcrypt(const u8* cipher, const u8* key, u32 key_len,
void* iv, void* dst, void* src, size_t src_len, cipher_mode mode)
{
struct scatterlist src_sg, dst_sg;
struct crypto_skcipher* tfm;
struct skcipher_request* req = 0;
struct tcrypt_result crypt_res;
int res = -EFAULT;
tfm = crypto_alloc_skcipher(cipher, 0, 0);
if (IS_ERR(tfm))
{
printk(KERN_WARNING "Failed to initialize skcipher mode %s\n", cipher);
res = PTR_ERR(tfm);
tfm = NULL;
goto out;
}
req = skcipher_request_alloc(tfm, GFP_NOFS);
if (!req)
{
printk(KERN_WARNING "Couldn't allocate skcipher handle\n");
res = -ENOMEM;
goto out;
}
skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
crypt_complete_cb, &crypt_res);
if (crypto_skcipher_setkey(tfm, key, key_len))
{
printk(KERN_WARNING "Failed to set key\n");
res = -EINVAL;
goto out;
}
sg_init_one(&src_sg, src, src_len);
sg_init_one(&dst_sg, dst, src_len);
skcipher_request_set_crypt(req, &src_sg, &dst_sg, src_len, iv);
init_completion(&crypt_res.completion);
if (mode == ENCRYPT)
res = crypto_skcipher_encrypt(req);
else
res = crypto_skcipher_decrypt(req);
switch (res)
{
case 0: break;
case -EINPROGRESS:
case -EBUSY:
wait_for_completion(&crypt_res.completion);
if (!res && !crypt_res.err)
{
reinit_completion(&crypt_res.completion);
break;
}
default:
printk("Skcipher %scrypt returned with err = %d, result %#x\n",
mode == ENCRYPT ? "en" : "de", res, crypt_res.err);
break;
}
out:
if (tfm)
crypto_free_skcipher(tfm);
if (req)
skcipher_request_free(req);
return res;
}
#define MASTER_KEY_SIZE 64
static int GetMasterKey(const u8* descriptor, u8* raw)
{
struct key* keyring_key = NULL;
const struct user_key_payload* ukp;
struct fscrypt_key* master_key;
keyring_key = request_key(&key_type_logon, descriptor, NULL);
if (IS_ERR(keyring_key))
return -EINVAL;
if (keyring_key->type != &key_type_logon)
{
printk_once(KERN_WARNING "%s: key type must be 'logon'\n", __func__);
return -EINVAL;
}
down_read(&keyring_key->sem);
ukp = user_key_payload(keyring_key);
master_key = (struct fscrypt_key*)ukp->data;
up_read(&keyring_key->sem);
if (master_key->size != MASTER_KEY_SIZE)
{
printk(KERN_WARNING "Wrong Master key size %#x\n", master_key->size);
return -EINVAL;
}
memcpy(raw, master_key->raw, master_key->size);
return 0;
}
int err;
u8 iv[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
u8 nonce_dir[16] = { ... };
u8 master_key[64], derived_key[64];
u8 dec_file_name[] = { ... };
u8 enc_file_name[sizeof(dec_file_name)];
err = do_blkcrypt("ecb(aes)", nonce_dir, 16, iv, derived_key, master_key,
MASTER_KEY_SIZE, ENCRYPT);
if (err)
return err;
err = do_skcrypt("cts(cbc(aes))", derived_key, MASTER_KEY_SIZE / 2, iv,
dec_file_name, enc_file_name, sizeof(dec_file_name), DECRYPT);
return err;
u8 nonce_file[16] = { ... };
u8 enc_file_data[PAGE_SIZE] = { ... };
u8 dec_file_data[PAGE_SIZE];
err = do_blkcrypt("ecb(aes)", nonce_file, 16, iv, derived_key, master_key,
MASTER_KEY_SIZE, ENCRYPT);
if (err)
return err;
err = do_skcrypt("xts(aes)", derived_key, MASTER_KEY_SIZE, iv,
dec_file_data, enc_file_data, PAGE_SIZE, DECRYPT);
return err;
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/fscrypto.h>
obj-m += ciphertest.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
u8 master_key[MASTER_KEY_SIZE] = {
0xa5, 0xb5, 0xc9, 0x23, 0x02, 0x14, 0xfc, 0xf7,
0x28, 0xdc, 0x90, 0x25, 0x24, 0x9e, 0xe6, 0xbc,
0x7c, 0xa8, 0xf8, 0xe1, 0x94, 0xf6, 0x67, 0x32,
0x33, 0xc4, 0xc1, 0xe8, 0x78, 0x59, 0xab, 0xfb,
0xae, 0xb0, 0xbf, 0x5d, 0x2c, 0x69, 0xc3, 0x8f,
0x51, 0x37, 0x26, 0x3f, 0xd1, 0xce, 0x37, 0xef,
0x3f, 0x80, 0xe3, 0x2d, 0xd5, 0xfd, 0x78, 0x45,
0x62, 0xf3, 0xa5, 0x24, 0x6b, 0xcf, 0x4a, 0x88
};
u8 enc_file_name[] = {
0x41, 0xa8, 0x4e, 0x4d, 0xd4, 0x1c, 0x43, 0x00,
0xa7, 0x5a, 0x2f, 0xd5, 0xaa, 0xa0, 0x5d, 0xb0
};
u8 nonce_dir[] = {
0x37, 0xba, 0x14, 0x16, 0x3e, 0xa8, 0xd5, 0x48,
0xd1, 0x3c, 0xb5, 0x6a, 0x01, 0xb7, 0x7c, 0x41
};
u8 nonce_file[] = {
0x61, 0x63, 0xb8, 0x31, 0xf4, 0xf5, 0xfc, 0x99,
0x1e, 0x3c, 0xf1, 0x8a, 0x23, 0xaf, 0x1e, 0xa8
};
К сожалению, не доступен сервер mySQL