
Р.П.408112-0 ХХ 01
УТВЕРЖДЕНО
Р.П.408112-07 12 01-лу
Драйвер файловой системы с шифрованием т екст программы
Р.П.408112-07 12 01
Аннотация
В настоящем документе приведен текст программы «CryptoFS», предназначенной для прозрачного шифрования файлов. Текст программы реализован в виде символической записи на исходном языке. Исходным языком данной разработки является ANSI C. Среда разработки – NetBeans. Компилятор – GCC.
Программа обеспечивает возможность выполнения перечисленных ниже функций:
загрузка открытых и/или закрытых ключей;
передача подсистеме FUSE параметров, необходимых для монтирования заданной файловой системы;
переопределение набора функций, реализующих операции с файлами, каталогами и атрибутами файлов;
реализация шифрования файлов с использованием симметричного алгоритма AES и ассиметричного алгоритма RSA.
Содержание
Текст программы 4
Лист регистрации изменений 14
Текст программы
#define FUSE_USE_VERSION 26
#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <dirent.h>
struct public_key {
RSA * key;
unsigned char fingerprint[20];
uid_t owner;
gid_t owner_group;
};
static char mount_dir[256];
static RSA * private_key = NULL;
static unsigned char private_key_fingerprint[20];
static struct public_key * public_keys = NULL;
static int public_keys_count = 0;
void get_fingerprint(RSA * key, unsigned char * fingerprint) {
SHA_CTX finger;
SHA1_Init(&finger);
int size = BN_num_bytes(key->n);
unsigned char * num = malloc(size);
BN_bn2bin(key->n, num);
SHA1_Update(&finger, num, size);
size = BN_num_bytes(key->e);
BN_bn2bin(key->e, num);
SHA1_Update(&finger, num, size);
SHA1_Final(fingerprint, &finger);
free(num);
}
int check_permissions(int fd) {
struct stat f_stat;
if (fstat(fd, &f_stat) == -1) {
return -errno;
}
if (S_ISDIR(f_stat.st_mode)) {
return 0;
}
char fingerprint[20];
if (pread(fd, fingerprint, 20, 0) < 20) {
return -ENOENT;
}
if (private_key == NULL ||
memcmp(fingerprint, private_key_fingerprint, 20) != 0) {
return -EACCES;
}
return 0;
}
static int crypto_open(const char * path, struct fuse_file_info * fi) {
char * full_path = malloc(strlen(mount_dir) + strlen(path) + 2);
sprintf(full_path, "%s%s", mount_dir, path);
int mode = fi->flags;
if ((mode & 0x3) == O_WRONLY) {
mode = (mode & (~ 0x3)) | O_RDWR;
}
mode &= ~ O_APPEND;
int fd = open(full_path, mode);
free(full_path);
if (fd == -1) {
return -errno;
}
int rc;
if ((rc = check_permissions(fd)) != 0) {
close(fd);
return rc;
}
fi->fh = fd;
return 0;
}
static int crypto_release(const char * path, struct fuse_file_info * fi) {
return close(fi->fh);
}
static int crypto_mknod(const char * path, mode_t mode, dev_t rdev) {
RSA * key = private_key;
unsigned char * key_fingerprint = private_key_fingerprint;
if (private_key == NULL) {
int no_key = 1;
int i;
uid_t my_uid = getuid();
for (i = 0; i < public_keys_count; i++) {
if (public_keys[i].owner == my_uid) {
no_key = 0;
key = public_keys[i].key;
key_fingerprint = public_keys[i].fingerprint;
break;
}
}
if (no_key) {
return -EACCES;
}
}
if (!S_ISREG(mode)) {
return -EPERM;
}
char * full_path = malloc(strlen(mount_dir) + strlen(path) + 2);
sprintf(full_path, "%s%s", mount_dir, path);
if (mknod(full_path, mode, rdev) == -1) {
free(full_path);
return -errno;
}
int fd = open(full_path, O_WRONLY);
free(full_path);
if (fd == -1) {
return -errno;
}
write(fd, key_fingerprint, 20);
write(fd, "\0", 1);
unsigned char AES_key[32];
int size = RSA_size(key);
unsigned char * AES_key_encrypted = malloc(size);
RAND_bytes(AES_key, 32);
RSA_public_encrypt(32, AES_key, AES_key_encrypted, key, RSA_PKCS1_OAEP_PADDING);
write(fd, AES_key_encrypted, size);
free(AES_key_encrypted);
close(fd);
return 0;
}
static int crypto_read(const char * path, char * buf, size_t size, off_t offset, struct fuse_file_info * fi) {
size_t len;
struct stat f_stat;
if (fstat(fi->fh, &f_stat) == -1) {
return -errno;
}
char end_pad_size;
if (pread(fi->fh, &end_pad_size, 1, 20) < 1){
return -ENODATA;
}
int header_size = 21 + RSA_size(private_key);
len = f_stat.st_size;
if (header_size + end_pad_size > len) {
return -ENODATA;
}
len -= header_size;
if ((len & 0xF) != 0) {
return -ENODATA;
}
len -= end_pad_size;
if (offset >= len) {
return 0;
}
if (offset + size > len) {
size = len - offset;
}
int key_size = RSA_size(private_key);
unsigned char * AES_key = malloc(key_size);
unsigned char * AES_key_encrypted = malloc(key_size);
if (pread(fi->fh, AES_key_encrypted, key_size, 21) < key_size){
return -ENODATA;
}
if (RSA_private_decrypt(key_size, AES_key_encrypted, AES_key, private_key, RSA_PKCS1_OAEP_PADDING) != 32) {
return -ENODATA;
}
AES_KEY decrypt_key;
AES_set_decrypt_key(AES_key, 256, &decrypt_key);
memset(AES_key, 0, key_size);
free(AES_key);
free(AES_key_encrypted);
unsigned char file_block[16];
unsigned char decrypted_file_block[16];
int block_offset = (offset & (~ 0xF)) + header_size;
size_t read_size = 0;
int start_offset = offset & 0xF;
while (read_size < size) {
if (pread(fi->fh, file_block, 16, block_offset) < 16){
break;
}
AES_decrypt(file_block, decrypted_file_block, &decrypt_key);
int read_block_size = 16 - start_offset;
if (read_size + read_block_size > size) {
read_block_size = size - read_size;
}
memcpy(buf + read_size, decrypted_file_block + start_offset, read_block_size);
read_size += read_block_size;
block_offset += 16;
start_offset = 0;
}
return read_size;
}
static int crypto_write(const char * path, const char * buf, size_t size, off_t offset, struct fuse_file_info * fi) {
size_t len;
struct stat f_stat;
if (fstat(fi->fh, &f_stat) == -1) {
return -errno;
}
char end_pad_size;
if (pread(fi->fh, &end_pad_size, 1, 20) < 1){
return -ENODATA;
}
int header_size = 21 + RSA_size(private_key);
len = f_stat.st_size;
if (header_size + end_pad_size > len) {
return -ENODATA;
}
len -= header_size;
if ((len & 0xF) != 0) {
return -ENODATA;
}
len -= (end_pad_size > 0 ? 16 : 0);
int key_size = RSA_size(private_key);
unsigned char * AES_key = malloc(key_size);
unsigned char * AES_key_encrypted = malloc(key_size);
if (pread(fi->fh, AES_key_encrypted, key_size, 21) < key_size){
return -ENODATA;
}
if (RSA_private_decrypt(key_size, AES_key_encrypted, AES_key, private_key, RSA_PKCS1_OAEP_PADDING) != 32) {
return -ENODATA;
}
AES_KEY decrypt_key;
AES_KEY encrypt_key;
AES_set_decrypt_key(AES_key, 256, &decrypt_key);
AES_set_encrypt_key(AES_key, 256, &encrypt_key);
memset(AES_key, 0, key_size);
free(AES_key);
free(AES_key_encrypted);
unsigned char file_block[16];
unsigned char encrypted_file_block[16];
int block_offset = offset & (~ 0xF);
size_t write_size = 0;
int start_offset = offset & 0xF;
while (write_size < size) {
int write_block_size = 16 - start_offset;
if (write_size + write_block_size > size) {
write_block_size = size - write_size;
}
if (block_offset < len + end_pad_size && (start_offset > 0 || write_size + write_block_size > size)) {
if (pread(fi->fh, encrypted_file_block, 16, block_offset + header_size) < 16){
break;
}
AES_decrypt(encrypted_file_block, file_block, &decrypt_key);
}
if (block_offset >= len) {
end_pad_size = (char)(16 - write_block_size - start_offset);
}
memcpy(file_block + start_offset, buf + write_size, write_block_size);
AES_encrypt(file_block, encrypted_file_block, &encrypt_key);
if (pwrite(fi->fh, encrypted_file_block, 16, block_offset + header_size) < 16){
break;
}
if (pwrite(fi->fh, &end_pad_size, 1, 20) < 1){
break;
}
write_size += write_block_size;
block_offset += 16;
start_offset = 0;
}
return write_size;
}
static int crypto_chown(const char * path, uid_t uid, gid_t gid) {
char * full_path = malloc(strlen(mount_dir) + strlen(path) + 2);
sprintf(full_path, "%s%s", mount_dir, path);
struct stat f_stat;
stat(full_path, &f_stat);
int fd = open(full_path, O_RDWR);
free(full_path);
if (fd == -1) {
return -errno;
}
int rc;
if ((rc = check_permissions(fd)) != 0) {
close(fd);
return rc;
}
int old_key_size = RSA_size(private_key);
if (((f_stat.st_size - 21 - old_key_size) & 0xF) != 0) {
return -ENODATA;
}
int i;
for (i = 0; i < public_keys_count; i++) {
if (public_keys[i].owner == uid) {
unsigned char file_block[16];
unsigned char decrypted_file_block[16];
pwrite(fd, public_keys[i].fingerprint, 20, 0);
unsigned char * old_AES_key = malloc(old_key_size);
unsigned char new_AES_key[32];
int new_key_size = RSA_size(public_keys[i].key);
unsigned char * old_AES_key_encrypted = malloc(old_key_size);
unsigned char * new_AES_key_encrypted = malloc(new_key_size);
if (pread(fd, old_AES_key_encrypted, old_key_size, 21) < old_key_size) {
return -ENODATA;
}
if (RSA_private_decrypt(old_key_size, old_AES_key_encrypted, old_AES_key, private_key, RSA_PKCS1_OAEP_PADDING) != 32) {
return -ENODATA;
}
RAND_bytes(new_AES_key, 32);
AES_KEY decrypt_key;
AES_KEY encrypt_key;
AES_set_decrypt_key(old_AES_key, 256, &decrypt_key);
AES_set_encrypt_key(new_AES_key, 256, &encrypt_key);
int j;
if (old_key_size >= new_key_size) {
j = 21;
} else {
j = f_stat.st_size - old_key_size - 16;
}
while (1) {
if (old_key_size >= new_key_size) {
if (j + old_key_size >= f_stat.st_size) {
break;
}
} else if (j < 21) {
break;
}
if (pread(fd, file_block, 16, j + old_key_size) < 16) {
return -ENODATA;
}
AES_decrypt(file_block, decrypted_file_block, &decrypt_key);
AES_encrypt(decrypted_file_block, file_block, &encrypt_key);
if (pwrite(fd, file_block, 16, j + new_key_size) < 16) {
return -ENODATA;
}
if (old_key_size >= new_key_size) {
j += 16;
} else {
j -= 16;
}
}
RSA_public_encrypt(32, new_AES_key, new_AES_key_encrypted, public_keys[i].key, RSA_PKCS1_OAEP_PADDING);
pwrite(fd, new_AES_key_encrypted, new_key_size, 21);
if (new_key_size < old_key_size) {
if (ftruncate(fd, f_stat.st_size - old_key_size + new_key_size) == -1) {
return -errno;
}
}
free(old_AES_key);
free(old_AES_key_encrypted);
free(new_AES_key_encrypted);
close(fd);
return 0;
}
}
close(fd);
return -EACCES;
}
static void crypto_destroy(void * private_data){
RSA_free(private_key);
int i;
for (i = 0; i < public_keys_count; i++) {
RSA_free(public_keys[i].key);
}
free(public_keys);
}
static struct fuse_operations crypto_oper = {
.getattr = crypto_getattr,
.fgetattr = crypto_fgetattr,
.readdir = crypto_readdir,
.open = crypto_open,
.release = crypto_release,
.mknod = crypto_mknod,
.read = crypto_read,
.write = crypto_write,
.ftruncate = crypto_ftruncate,
.truncate = crypto_truncate,
.access = crypto_access,
.mkdir = crypto_mkdir,
.unlink = crypto_unlink,
.rmdir = crypto_rmdir,
.rename = crypto_rename,
.chmod = crypto_chmod,
.chown = crypto_chown,
.utimens = crypto_utimens,
.fsync = crypto_fsync,
.statfs = crypto_statfs,
.destroy = crypto_destroy
};
void load_public_key(char * key_file_name, int * pubkeys_array_size, struct stat * f_stat) {
int i;
for (i = 0; i < public_keys_count; i++) {
if (public_keys[i].owner == f_stat->st_uid) {
fprintf(stderr, "Ignore public key from file %s: public key "
"with same owner already load\n", key_file_name);
return;
}
}
int array_size = *pubkeys_array_size;
if (public_keys_count == array_size) {
array_size += 16;
void * rc = realloc(public_keys, array_size * sizeof(struct public_key));
if (rc == NULL) {
fprintf(stderr, "Can't allocate enough memory for public keys array\n");
return;
} else {
public_keys = rc;
}
}
*pubkeys_array_size = array_size;
FILE * key_file = fopen(key_file_name, "r");
if (key_file == NULL) {
fprintf(stderr, "Can't open file %s: %s\n", key_file_name,
strerror(errno));
return;
}
RSA * key = PEM_read_RSA_PUBKEY(key_file, NULL, NULL, NULL);
fclose(key_file);
if (key == NULL) {
fprintf(stderr, "Can't read public key from file %s: %s\n", key_file_name,
ERR_reason_error_string(ERR_get_error()));
return;
}
unsigned char fingerprint[20];
get_fingerprint(key, fingerprint);
for (i = 0; i < public_keys_count; i++) {
if (memcmp(fingerprint, public_keys[i].fingerprint, 20) == 0) {
fprintf(stderr, "Ignore public key from file %s: public key "
"with same fingerprint already load\n", key_file_name);
return;
}
}
public_keys[public_keys_count].key = key;
memcpy(public_keys[public_keys_count].fingerprint, fingerprint, 20);
public_keys[public_keys_count].owner = f_stat->st_uid;
public_keys[public_keys_count].owner_group = f_stat->st_gid;
public_keys_count++;
}
int main(int argc, char ** argv) {
int opt;
int pubkeys_array_size = 0;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
while ((opt = getopt(argc, argv, "k:p:l:h")) != -1) {
struct stat f_stat;
switch (opt) {
case 'k':
if (private_key != NULL) {
fprintf(stderr, "Only 1 private key need\n");
break;
}
FILE * key_file = fopen(optarg, "r");
if (key_file == NULL) {
fprintf(stderr, "Can't open file %s: %s\n", optarg,
strerror(errno));
break;
}
RSA * key = PEM_read_RSAPrivateKey(key_file, NULL, NULL, NULL);
fclose(key_file);
if (key == NULL) {
fprintf(stderr, "Can't read private key from file %s: %s\n", optarg,
ERR_reason_error_string(ERR_get_error()));
break;
}
private_key = key;
get_fingerprint(private_key, private_key_fingerprint);
break;
case 'p':
stat(optarg, &f_stat);
load_public_key(optarg, &pubkeys_array_size, &f_stat);
break;
case 'l':
stat(optarg, &f_stat);
if (!S_ISDIR(f_stat.st_mode)) {
fprintf(stderr, "Error while open library %s: "
"not a directory\n", optarg);
break;
}
struct dirent ** dir;
int n;
if ((n = scandir(optarg, &dir, NULL, NULL)) < -1) {
fprintf(stderr, "Can't open file %s: %s\n", optarg,
strerror(errno));
break;
}
int i;
for (i = 0; i < n; i++) {
stat(dir[i]->d_name, &f_stat);
if (S_ISREG(f_stat.st_mode)) {
load_public_key(dir[i]->d_name, &pubkeys_array_size, &f_stat);
}
free(dir[i]);
}
free(dir);
break;
case 'h':
default:
fprintf(stderr, "Usage: %s [options] mount_dir mount_point\n",
argv[0]);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -k private_key - Path to private key file\n");
fprintf(stderr, " -p public_key - Path to public key file\n");
fprintf(stderr, " -l library - Path to dir with public keys\n");
fprintf(stderr, " -h - Show this message\n");
exit(EXIT_FAILURE);
}
}
if (optind >= argc - 1) {
fprintf(stderr, "Expected mount dir and mount point after options\n");
exit(EXIT_FAILURE);
}
if (private_key != NULL) {
printf("Private key loaded\n");
}
if (public_keys_count > 0) {
printf("%d public key%s loaded\n", public_keys_count,
public_keys_count == 1 ? "" : "s");
}
if (chdir(argv[argc - 2]) == -1) {
fprintf(stderr, "Wrong mount point: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
getcwd(mount_dir, 255);
argv[1] = argv[argc - 1];
umask(0);
return fuse_main(2, argv, &crypto_oper, NULL);
}
Лист регистрации изменений |
|||||||||
Номера листов (страниц) |
Всего листов (страниц) в докум |
№ документа |
Входящий № сопрово дительного документа и дата |
Подп. |
Дата |
||||
Изм |
изменен ных |
заме ненных |
новых |
анулиро ванных |
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|