C 中的 Xor 加密?

Xor encryption in C?

我尝试学习 C 中的 XOR,我在 github 上找到了 this example

#include <stdio.h>
#include <string.h>

void encryptDecrypt(char *input, char *output) {
    char key[] = {'K', 'C', 'Q'}; //Can be any chars, and any size array
    
    int i;
    for(i = 0; i < strlen(input); i++) {
        output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];
    }
}

int main (int argc, char *argv[]) {
    char baseStr[] = "kylewbanks.com";
    
    char encrypted[strlen(baseStr)];
    encryptDecrypt(baseStr, encrypted);
    printf("Encrypted:%s\n", encrypted);
    
    char decrypted[strlen(baseStr)];
    encryptDecrypt(encrypted, decrypted);
    printf("Decrypted:%s\n", decrypted);
}

你能解释一下这个说法吗?

output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];

PS:如果我更改char baseStr[]中的语句输出是错误的,为什么?例如,如果 baseStr 的值为 Test,则输出将为:

Encrypted: *?�
Decrypted:Test�&

编辑

如果有人想要这个程序的工作版本,这里是:
#include <stdio.h>
#include <string.h>
#include <ctype.h>

void encryptDecrypt(char*, char*, char*);

int main()
{
    char message[500];
    char key[50];

    printf("Enter the message: ");
    fgets(message, 500, stdin);
    message[strcspn(message, "\n")] = 0;
    
    printf("Enter the key: ");
    fgets(key, 50, stdin);
    key[strcspn(key, "\n")] = 0;
    
    for(size_t i = 0; i < strlen(key); ++i)
        key[i] = toupper (key[i]);

    putchar('\n');

    char encryptedMessage[strlen(message) + 1];
    memset(encryptedMessage, '[=13=]', sizeof(encryptedMessage));
    encryptDecrypt(message, encryptedMessage, key);
    printf("Encrypted message: %s\n", encryptedMessage);

    char decryptedMessage[strlen(message) + 1];
    memset(decryptedMessage, '[=13=]', sizeof(decryptedMessage));
    encryptDecrypt(encryptedMessage, decryptedMessage, key);
    printf("Decrypted message: %s\n", decryptedMessage);

    return 0;
}

void encryptDecrypt(char *input, char *output, char *key)
{
    for (int i = 0; i < strlen(input); ++i)
        output[i] = input [i] ^ key [i % strlen(key)];
}

%s 格式说明符用于 C 风格的字符串。您不能将它用于任意字符数组。如您所见,它无法知道要打印多少个字符。

那个代码很糟糕。它在其输入上调用 strlen。所以它的输入必须是一个C风格的字符串。但它的输出不是 C 风格的字符串,它只是一个字符数组。缺乏对称性使得代码难以使用和理解。

一块一块地分解:

  1. (sizeof(key)/sizeof(char)) 这是key数组的长度。在你的例子中是 3。注意,sizeof(char) 是多余的,可以省略。
  2. key[i % 3] 这将 key 的索引绑定到 012,因此不会访问 [=13= 之外的内存]数组。
  3. output[i] = input[i] ^ key[i % 3];您将 input 中的每个 char 角色与 key 进行异或并将其分配给 output.

注意,如果你想打印 encrypteddecrypted 你应该这样定义它们:

char encrypted[strlen(baseStr) + 1] = {'[=10=]'};
char decrypted[strlen(baseStr) + 1] = {'[=10=]'};

编辑

根据 user3386109声明 VLA 时不允许提供初始值设定项。因此,您应该执行(例如)以下操作:

memset(encrypted, '[=11=]', sizeof(encrypted));
memset(decrypted, '[=11=]', sizeof(decrypted));

对于:

output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];

sizeof(key)/sizeof(char) 真正得到的是列表中的元素个数:3/1

I % numelemement 得到 I/numelements 的余数 - 所以在这种情况下,当你上升到 I 位置时,将是 0,1,2,0,1,2(在这种情况下)

所以

output_value = input_value 异或密钥[余数]

您不是以空值终止输入字符串,因此 strlen 调用与原始字符串不同。

PS: If I change the statement in char baseStr[] the output is wrong, why? For example if the value of baseStr is Test the output will be:

Encrypted: *?�
Decrypted:Test�&

这是因为 encryptDecrypt 正在使用 strlen 来确定“字符串”的结尾。 strlen 依靠读取空字节来确定字符串的结尾。加密时,您有一个以空字符结尾的字符串。但是加密不会产生以空值结尾的字符串,它会产生一个不以空值结尾的字节流。

我们可以解决这个问题。

void encryptDecrypt(char *input, char *output) {
    char key[] = {'K', 'C', 'Q'}; //Can be any chars, and any size array

    size_t len = strlen(input);
    for(size_t i = 0; i < len; i++) {
        printf("%zu\n", i);
        output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];
    }
    output[len] = '[=11=]';
}

我不知道这是否是一个很好的修复。 strlen 可能对解密不安全,因为加密字符串的一个字节可能包含空字节。函数取输入的长度可能更聪明。

void encryptDecrypt(char *input, char *output, size_t *strlen)

// We know baseStr is a string and can use strlen.
char encrypted[strlen(baseStr)];
encryptDecrypt(baseStr, encrypted, strlen(baseStr));

// The encrypted string is the same length as `baseStr`.
char decrypted[strlen(baseStr)];
encryptDecrypt(encrypted, decrypted, strlen(baseStr));

output[i] = input[i] ^ key[i % (sizeof(key)/sizeof(char))];

(sizeof(key)/sizeof(char)) 正在获取 key 中的元素数。 sizeof 产生字节数。因此 (sizeof(key)/sizeof(char)) 通过将数组的大小除以每个元素的大小来得出元素的数量。

char 总是 1 个字节,我们可以去掉 sizeof(char) 让事情变得更简单。

output[i] = input[i] ^ key[i % sizeof(key)];

这是对 input 的每个字符与 key 的每个字符进行异或运算。因为keyinput可能是不同的长度,而key可能更短, it must wrap around and reuse the keyagain.%`是取模运算符,所以会除以余数

  • 0 % 3 = 0
  • 1 % 3 = 1
  • 2%3=2
  • 3 % 3 = 0
  • 4 % 3 = 1

这也称为“时钟数学”。

这样,input 的第一个字符与 key 的第一个字符进行异或运算,第二个字符与第二个字符进行异或运算,第三个字符与第三个字符进行异或运算,但 [=4] 的第四个字符进行异或运算。 =26=] 与 key.

的第一个字符异或

您可以通过打印 i 和密钥索引来查看实际效果。

    for(size_t i = 0; i < len; i++) {
        printf("%zu\n", i);
        size_t ki = i % sizeof(key);
        printf("i = %zu, ki = %zu\n", i, ki);
        output[i] = input[i] ^ key[ki];
    }