1

所以,我试图加密和解密一个字符串,在arch上使用libgcrypt库(版本1.8.7),此时我尝试了2种模式:CBC和GCM(不确定GCM,所以让我们先解决CBC),但同样的问题出现了。

我填充字符串,然后逐块对其进行加密。有时,顺便说一句,这会发生混乱,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 

我真的很抱歉这个烂摊子,只是我在这一点上发疯了,看起来解决方案很简单,但我就是无法理解。

4

1 回答 1

0

strlen不会告诉您有关输出缓冲区长度的任何信息。实际上,您的输出缓冲区始终是相同的长度。无需测试它的长度,因为 libgcrypt 无法修改它的长度。

如果您想了解为什么strlen返回“混乱”值,您需要了解strlen打算做什么。strlen旨在对 C 样式(以空结尾)字符串进行操作,而不是对任意字节进行操作。'\0'C 中的字符串存储为以( 0x00) 字符结尾的字符数组。这是空终止符。这就是如何确定 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 任何地方包含该字节。它可能出现在开头或中间的任何地方。它可能会出现好几次。或者它永远不会出现,在这种情况下你会得到一个致命的分段错误。无论0x00您的密文中第一次出现在哪里,都将是strlen假设它结束的地方。这种行为显得“混乱”,因为加密会产生看似随机的数据,因此该数据中的分布0x00也是看似随机的。

PS:你不需要每次加密一个块时都重置密钥。

于 2020-12-12T10:57:40.373 回答