我正在使用AES加密和OpenSSL开发加密/解密程序。我在我的linux虚拟机上的两个本地目录之间设置了一个FUSE文件系统,一个是镜像目录,另一个是挂载点。我正在修改fuse_operations结构中的读取,写入和创建函数,这样当我从挂载点目录打开并读取加密文件时,明文应该显示在应用程序的窗口中。
我有一个名为do_crypt的函数,它将带有文本的文件作为输入,输出文件应该是解密/加密的文本,用于解密,加密或传递的int,以及用于完成的短语行动。代码如下:
#define BLOCKSIZE 1024
#define FAILURE 0
#define SUCCESS 1
extern int do_crypt(FILE* in, FILE* out, int action, char* key_str){
/* Local Vars */
/* Buffers */
unsigned char inbuf[BLOCKSIZE];
int inlen;
/* Allow enough space in output buffer for additional cipher block */
unsigned char outbuf[BLOCKSIZE + EVP_MAX_BLOCK_LENGTH];
int outlen;
int writelen;
/* OpenSSL libcrypto vars */
EVP_CIPHER_CTX ctx;
unsigned char key[32];
unsigned char iv[32];
int nrounds = 5;
/* tmp vars */
int i;
/* Setup Encryption Key and Cipher Engine if in cipher mode */
if(action >= 0){
if(!key_str){
/* Error */
fprintf(stderr, "Key_str must not be NULL\n");
return 0;
}
/* Build Key from String */
i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL,
(unsigned char*)key_str, strlen(key_str), nrounds, key, iv);
if (i != 32) {
/* Error */
fprintf(stderr, "Key size is %d bits - should be 256 bits\n", i*8);
return 0;
}
/* Init Engine */
EVP_CIPHER_CTX_init(&ctx);
EVP_CipherInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv, action);
}
/* Loop through Input File*/
for(;;){
/* Read Block */
inlen = fread(inbuf, sizeof(*inbuf), BLOCKSIZE, in);
if(inlen <= 0){
/* EOF -> Break Loop */
break;
}
/* If in cipher mode, perform cipher transform on block */
if(action >= 0){
if(!EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
}
/* If in pass-through mode. copy block as is */
else{
memcpy(outbuf, inbuf, inlen);
outlen = inlen;
}
/* Write Block */
writelen = fwrite(outbuf, sizeof(*outbuf), outlen, out);
if(writelen != outlen){
/* Error */
perror("fwrite error");
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
}
/* If in cipher mode, handle necessary padding */
if(action >= 0){
/* Handle remaining cipher block + padding */
if(!EVP_CipherFinal_ex(&ctx, outbuf, &outlen))
{
/* Error */
EVP_CIPHER_CTX_cleanup(&ctx);
return 0;
}
/* Write remainign cipher block + padding*/
fwrite(outbuf, sizeof(*inbuf), outlen, out);
EVP_CIPHER_CTX_cleanup(&ctx);
}
/* Success */
return 1;
}
该功能在不使用保险丝系统的情况下工作正常。一切都是正确加密/解密的,但当我使用读取功能解密保险丝文件系统中的文件时,我在输出中得到一些无效字符。我已经尝试了3个文本编辑器来查看文件:sublime读取输出就像任何其他文件一样没有问题; geany不会打开该文件,因为它表示它无法识别编码并且存在无效字符; gedit显示解密输出,但也显示一些表示为'/ 00/00/00'的无效字符。我不知道这些人物是什么。我做了一些研究,我相信它们是空字符,但我不知道为什么它们出现在我使用保险丝功能并运行解密但它们在不使用它时不会出现在输出中。
我为读取功能尝试了两种不同的版本:一种使用内存缓冲区来保存内容的方法。
static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
(void) fi;
int res;
char fpath[PATH_MAX];
xmp_fullpath(fpath, path);//changes path to mirror directory
FILE *f;
FILE *tmpFile;
tmpFile = tmpfile();
f = fopen(fpath, "r");
do_crypt(f, tmpFile, DECRYPT, XMP_DATA->key_phrase);//function that decrypts/encrypts
rewind(tmpFile);
res = pread(fileno(tmpFile), buf, size, offset);//read the encrypted/decrypted output to the application buffer
fclose(f);
fclose(tmpFile);
return res;
}
另一种方法是在进行加密/解密之前创建一个临时文件:
int res;
char fpath[PATH_MAX];
xmp_fullpath(fpath, path);
FILE *memstream;
char *membuf;
size_t memlen;
//off_t eob;
memstream = open_memstream(&membuf, &memlen);//create a dynamically allocated buffer in memory
FILE *f = fopen(fpath, "rb");
do_crypt(f, memstream, DECRYPT, XMP_DATA->key_phrase);
fflush(memstream);
fseek(memstream, offset, SEEK_SET);
res = fread(buf, 1, memlen, memstream);
return res;
我的问题是:
1)为什么在尝试解密文件时,我的输出中出现无效字符'/ 00/00'似乎是空字符?
2)为什么我能够使用一个文本编辑器而不是另一个文本编辑器查看文件?
再次加密和传递在保险丝系统中完美地工作,我可以用所有三个文本编辑器打开它们。当我不使用保险丝时,do_crypt工作正常。但是当我尝试在保险丝读取功能中解密时,我得到了正确的解密文本,但我也得到了那些无效的字符。当我使用'cat'terminal命令时,输出不包含无效字符。
以下是完整代码库的链接。 https://github.com/latitude98/CU-CS3753-PA4
答案 0 :(得分:0)
在VFS级别(例如通过保险丝)进行加密/解密等数据突变的常见错误是在调用底层getattr()系统调用之前无法改变(即解密)fuse getattr函数中的文件。
在您的情况下,如果getattr()系统调用是通过加密文件而不是未加密文件传递的,则它将返回错误的文件大小(加密和解密文件具有不同的文件大小)。许多程序依赖于这个文件大小是正确的,以便知道要读取多少文件,因此如果getattr()返回加密文件的大小而不是未加密文件的大小,您可能会看到错误就像你描述的那样。
某些程序(例如复杂的文本编辑器)依赖于在读取文件时知道文件大小,因此需要工作的getattr()才能运行。像'cat'这样的实用程序倾向于使用更基本的“一次读取一个字节,直到我看到EOF”方法,因此不太依赖于正确的文件大小。这可以解释为什么有些程序运行正常(那些寻找EOF),但其他程序没有(那些试图读取字节)。
有一些VFS /保险丝功能,除了明显的(即读取)要求您调用解密。如上所述,getattr可能是最关键的一个。您可能还想查看truncate和open。根据您实现文件系统的方式,这些也可能需要一些解密感知。