因此,我正在尝试加密和解密一个字符串,使用libgcrypt库(版本1.8.7)在arch上,目前我已经尝试了两种模式: CBC和GCM (对GCM不确定,所以让我们先解决CBC ),但同样的问题出现了。
我对字符串进行padd,然后逐块加密。有时,这会混乱地发生,顺便说一句,gcry_cipher_encrypt函数返回错误的字节数量(5,7,11...),但如果我理解正确,输出应该是16字节(128位)。同样的事情也发生在解密上,我用完全相同的方式,一块一块地做。我在整个加密或解密过程中使用相同的GCRY处理程序,感觉我真的遗漏了一些东西……这里有一个例子,只在CBC模式下加密,以便更容易地发现问题。
代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gcrypt.h>
// Define cipher details
#define GCRY_CIPHER GCRY_CIPHER_AES256
#define GCRY_C_MODE GCRY_CIPHER_MODE_CBC
char * encrypt_block(gcry_cipher_hd_t handler, unsigned char * key, unsigned char * input) {
size_t key_length = 32;
size_t blk_length = 16;
// Encryption result variable
unsigned char * enc = (char *) calloc(16, sizeof(char));
// Error variable
gcry_error_t err = 0;
// Set key
err = gcry_cipher_setkey(handler, key, key_length);
if (err) {
printf("Couldn't set the key!\n%s\n%s\n", gcry_strsource(err), gcry_strerror(err));
exit(-1);
}
// Start encryption process
err = gcry_cipher_encrypt(handler, enc, blk_length, input, blk_length);
if (err) {
printf("Couldn't encrypt!\n%s\n%s\n", gcry_strsource(err), gcry_strerror(err));
exit(-1);
}
if (strlen(enc) != 16) {
printf("\n\nCORRUPTED BLOCK!\n\n");
}
// Printing the block result
printf("\nENC BLOCK:\t%d\t", strlen(enc));
for (unsigned short int i = 0; i < strlen(enc); ++i) {
printf("%X ", enc[i]);
}
printf("\n");
return enc;
}
int main() {
// Creating basic variables
unsigned char * input = (char *) calloc(2048, sizeof(char));
unsigned char * key = (char *) calloc(32, sizeof(char));
unsigned char * iv = (char *) calloc(16, sizeof(char));
// Taking user input
printf("Input (2048 max): ");
scanf(" %[^\n]", input);
printf("Key (32 max): ");
scanf(" %[^\n]", key);
printf("RAW DATA:\n\tinput: %d\t%s\n\tkey: %d\t%s\n\n", strlen(input), input, strlen(key), key);
// Create GCRY handler
gcry_cipher_hd_t handler;
gcry_error_t err = 0;
// Initialize cipher handler
err = gcry_cipher_open(&handler, GCRY_CIPHER, GCRY_C_MODE, 0);
if (err) {
printf("Couldn't initialize the cipher!\n%s\n%s\n", gcry_strsource(err), gcry_strerror(err));
exit(-1);
}
// Add padding to the input
if ((strlen(input) % 16) != 0) {
for (unsigned short int i = 0; i < (((strlen(input) / 16) * 16) - strlen(input)); ++i) {
strcat(input, "X");
}
}
// Add padding to the key
if (strlen(key) < 32) {
for (unsigned short int i = strlen(key); i < 32; ++i) {
key[i] = 0x0058;
}
}
// Generate random IV
char charset[] = "abcdefghijklmnopqrstuvwxyz0123456789";
unsigned short int iv_size = 16;
for (unsigned short int i = 0; i < iv_size; ++i) {
unsigned short int index = rand() % (unsigned short int) (sizeof charset - 1);
iv[i] = charset[index];
}
// Set the IV
err = gcry_cipher_setiv(handler, iv, 16);
if (err) {
printf("Couldn't set the IV!\n%s\n%s\n", gcry_strsource(err), gcry_strerror(err));
exit(-1);
}
printf("ENC DATA:\n\tinput: %d\t%s\n\tkey: %d\t%s\n\tiv: %d\t%s\n\n", strlen(input), input, strlen(key), key, strlen(iv), iv);
// Create encryption variables
unsigned char * input_buffer = (char *) calloc(16, sizeof(char));
unsigned char * enc_buffer = (char *) calloc(16, sizeof(char));
unsigned char * out = (char *) calloc(strlen(input), sizeof(char));
// Start encryption process block by block
for (unsigned short int i = 0; i < (strlen(input) / 16); ++i) {
// Create a new block
for (unsigned short int j = 0; j < 16; ++j) {
input_buffer[j] = input[(i * 16) + j];
}
printf("\nENC INPUT:\t%d\t%s\n", strlen(input_buffer), input_buffer);
// Check if this is a final round
if (i == ((strlen(input) / 16) - 1)) {
err = gcry_cipher_final(handler);
}
// Start encrypting the block
enc_buffer = encrypt_block(handler, key, input_buffer);
// Adding up the block to the out result
strcat(out, enc_buffer);
memset(input_buffer, 0, 16);
memset(enc_buffer, 0, 16);
}
// Print the encryption result
printf("\n\nENC RESULT:\n\t%d\n\t", strlen(out));
for (unsigned short int i = 0; i < strlen(out); ++i) {
printf("%X ", out[i]);
}
printf("\n");
gcry_cipher_close(handler);
}输出:
Input (2048 max): This string is made for testing the program
Key (32 max): hey my password
RAW DATA:
input: 43 This string is made for testing the program
key: 15 hey my password
ENC DATA:
input: 48 This string is made for testing the programXXXXX
key: 32 hey my passwordXXXXXXXXXXXXXXXXX
iv: 16 t8jhfhkm7bo5ohxw
ENC INPUT: 16 This string is m
ENC BLOCK: 16 2 BF AA A0 1 7C A8 77 DA 4A 5A 72 29 EB FA F6
ENC INPUT: 16 ade for testing
ENC BLOCK: 16 41 BA CE 61 8A E3 F4 89 8A 46 50 2 47 5 11 A4
ENC INPUT: 16 the programXXXXX
CORRUPTED BLOCK!
ENC BLOCK: 12 AE D6 92 D2 5A AF 85 CB 57 2 1B 93
ENC RESULT:
44
2 BF AA A0 1 7C A8 77 DA 4A 5A 72 29 EB FA F6 41 BA CE 61 8A E3 F4 89 8A 46 50 2 47 5 11 A4 AE D6 92 D2 5A AF 85 CB 57 2 1B 93 我真的很抱歉这个烂摊子,只是我在这一点上发疯了,看起来解决方案很简单,但我就是搞不懂。
发布于 2020-12-12 18:57:40
strlen不会告诉您任何有关输出缓冲区长度的信息。实际上,您的输出缓冲区总是相同的长度。没有必要测试它的长度,因为libgcrypt无法修改它的长度。
如果你想理解为什么strlen会返回“混乱”的值,你需要理解strlen的意图。strlen 旨在操作C样式(以null结尾)字符串,而不是任意字节。C中的字符串存储为以'\0' (0x00)字符结尾的字符数组。这是null终止符。这就是如何确定C字符串的长度。
// example implementation to explicate the concept
size_t strlen(const char *s) {
size_t i = 0;
while (s[i] != '\0')
++i;
return i;
}当您将strlen应用于任意字节时,结果是无意义的。您的二进制密文完全有可能在任何地方包含字节0x00。它可以出现在开头或中间的任何位置。它可能会出现多次。或者它永远不会出现,在这种情况下,你会得到一个致命的分段错误。strlen假定0x00在您的密文中首次出现的位置是它结束的地方。这种行为看起来是“混乱的”,因为加密产生了看似随机的数据,因此0x00在该数据中的分布也是随机的。
PS:您不需要在每次加密数据块时都重置密钥。
https://stackoverflow.com/questions/65209512
复制相似问题