首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C libgcrypt中的密文长度错误

C libgcrypt中的密文长度错误
EN

Stack Overflow用户
提问于 2020-12-09 09:50:46
回答 1查看 75关注 0票数 1

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

我对字符串进行padd,然后逐块加密。有时,这会混乱地发生,顺便说一句,gcry_cipher_encrypt函数返回错误的字节数量(5,7,11...),但如果我理解正确,输出应该是16字节(128位)。同样的事情也发生在解密上,我用完全相同的方式,一块一块地做。我在整个加密或解密过程中使用相同的GCRY处理程序,感觉我真的遗漏了一些东西……这里有一个例子,只在CBC模式下加密,以便更容易地发现问题。

代码:

代码语言:javascript
复制
#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);
}

输出:

代码语言:javascript
复制
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 

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

EN

回答 1

Stack Overflow用户

发布于 2020-12-12 18:57:40

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

如果你想理解为什么strlen会返回“混乱”的值,你需要理解strlen的意图。strlen 旨在操作C样式(以null结尾)字符串,而不是任意字节。C中的字符串存储为以'\0' (0x00)字符结尾的字符数组。这是null终止符。这就是如何确定C字符串的长度。

代码语言:javascript
复制
// 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:您不需要在每次加密数据块时都重置密钥。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65209512

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档