C中的维吉尼亚密码

Posted

技术标签:

【中文标题】C中的维吉尼亚密码【英文标题】:Vigenere cipher in C 【发布时间】:2015-09-06 20:36:56 【问题描述】:

我在调试 Vigenere 密码的 C 实现时遇到问题。使用文件输入(-f 标志)时出现错误,其中文件包含少于 6 个字符(+ 1 EOF),它会吐出一些随机字符以及预期的输入,我不知道为什么会这样,尽管我怀疑这与我的问题的第二部分有关,即在使用 fread() 时,我注意到这个

if( fread(fcontents, fsize, sizeof(char), file) != 1 ) ...

将毫无问题地运行,而这个

if( fread(fcontents, sizeof(char), fsize, file) != 1 ) ...

不起作用(即导致 fread() 返回 1 并触发其下方的错误处理代码),根据here 的答案,我希望相反,但我可能只是误解了东西。

我的完整代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>

#define ENC 0 //Encrypt mode
#define DEC 1 //Decrypt mode
#define INP 0 //Commandline input mode
#define FLE 1 //File input mode

typedef struct 
    char *password;
    char *file_name;
    char *input;
    int edmode;
    int ifmode;
 options;

void string_clean(char *source)

    char *i = source;
    char *j = source;

    while(*j != 0) 
        *i = *j++;
        if( *i != ' ' && (isupper(*i) || islower(*i)) )
            i++;
    

    *i = 0;


char *ftostr(char *file_name) //Takes a file name as input and returns a char* to the files contents, returns NULL pointer on faliure. Allocated string must be freed after use by parent function to prevent memory leaks.

    FILE *file;
    long int fsize;
    char *fcontents;

    if( !(file = fopen(file_name, "r")) ) 
        fprintf(stderr, "Error opening file \"%s\"!\n", file_name);
        return NULL;
    

    fseek(file, 0L, SEEK_END);
    fsize = ftell(file);
    rewind(file);

    if( !(fcontents = malloc((fsize + 1) * sizeof(char))) ) 
        fclose(file);
        fprintf(stderr, "Error allocating memory!");
        return NULL;
    

    if( fread(fcontents, fsize, sizeof(char), file) != 1 )  //suspected buggy line
        fclose(file);
        free(fcontents);
        fprintf(stderr, "Error copying file to memory!\n");
        return NULL;
    

    fclose(file);
    return fcontents;


options parse_opts(int argc, char *argv[])

    int c;
    options args;

    args.edmode     = ENC; //enable encrypt mode by default
    args.ifmode     = INP; //enable commandline input mode by default
    args.file_name  = NULL;
    args.password   = NULL;
    args.input      = NULL;
    opterr          = 0;

    while((c = getopt(argc, argv, "dep:i:f:")) != -1) 
        switch(c) 
            case 'e':
                args.edmode     = ENC;
                break;
            case 'd':
                args.edmode     = DEC;
                break;
            case 'p':
                args.password   = optarg;
                break;
            case 'i':
                args.input      = optarg;
                args.ifmode     = INP;
                break;
            case 'f':
                args.file_name  = optarg;
                args.ifmode     = FLE;
                break;
            case '?':
                if(optopt == 'f' || optopt == 'p' || optopt == 'i')
                    fprintf(stderr, "Option -%c requires an argument.\n", optopt);
                else if(isprint(optopt))
                    fprintf(stderr, "Unknown option `-%c'.\n", optopt);
                else
                    fprintf(stderr, "Unknown option character `\\x%x'.\n", optopt);

                fprintf(stderr, "Usage: %s (-f file_name || -i input) -p password [options]\n"
                                "Optional: -e -d\n", argv[0]);
                exit(-1);
        
    

    return args;


char *vigenere_dec(char cipher_text[], char cipher[])

    char *plain_text;

    string_clean(cipher_text);
    string_clean(cipher);

    int plain_text_len = strlen(cipher_text);
    int cipher_len = strlen(cipher);

    if( !(plain_text = malloc((plain_text_len + 1) * sizeof(char))) )
        return 0;

    for(int i = 0; i < cipher_len; i++) 
        if(isupper(cipher[i]))
            cipher[i] -= 'A';
        else if(islower(cipher[i]))
            cipher[i] -= 'a';
    

    for(int i = 0, j = 0; i < plain_text_len; i++, j++) 
        if(j == cipher_len)
            j = 0;

        if(isupper(cipher_text[i]))
            cipher_text[i] -= 'A';
        else if(islower(cipher_text[i]))
            cipher_text[i] -= 'a';

        plain_text[i] = ((cipher_text[i] - cipher[j]) % 26);
        if(plain_text[i] < 0)
            plain_text[i] += 26;
        plain_text[i] += 'A';
    
    return plain_text;


char *vigenere_enc(char plain[], char cipher[])

    char *cipher_text;

    string_clean(plain);
    string_clean(cipher);

    int plain_len   = strlen(plain);
    int cipher_len  = strlen(cipher);

    if(plain_len == 0 || cipher_len == 0)
        return NULL;

    if( !(cipher_text = malloc((plain_len + 1) * sizeof(char))) )
        return NULL;

    for(int i = 0; i < cipher_len; i++) 
        if(isupper(cipher[i]))
            cipher[i] -= 'A';
        else if(islower(cipher[i]))
            cipher[i] -= 'a';
    

    for(int i = 0, j = 0; i < plain_len; i++, j++) 
        if(j == cipher_len)
            j = 0;

        if(isupper(plain[i]))
            plain[i] -= 'A';
        else if(islower(plain[i]))
            plain[i] -= 'a';

        cipher_text[i] = ((plain[i] + cipher[j]) % 26) + 'A';
    
    return cipher_text;


int main(int argc, char *argv[])

    options args;
    char *output_text = NULL;

    args = parse_opts(argc, argv);

    if(args.password == NULL) 
        fprintf(stderr, "Password uninitialised!\n");
        exit(-1);
    

    if(args.input == NULL && args.file_name == NULL) 
        fprintf(stderr, "Input stream uninitialised!\n");
        exit(-1);
    

    if(args.ifmode == INP) 
        if(args.edmode == ENC)
            output_text = vigenere_enc(args.input, args.password);
        else if(args.edmode == DEC)
            output_text = vigenere_dec(args.input, args.password);
     else if(args.ifmode == FLE) 
        if( !(args.input = ftostr(args.file_name)) )
            return -1;
        if(args.edmode == ENC)
            output_text = vigenere_enc(args.input, args.password);
        else if(args.edmode == DEC)
            output_text = vigenere_dec(args.input, args.password);
        free(args.input);
    

    puts(output_text);
    free(output_text);

    return 0;

【问题讨论】:

EOF 不是char。请提供minimal reproducible example(注意最小!)并且“不起作用”不是错误描述。 “启用文件模式”是什么意思?除了要求的 MCVE 外,还请提供示例输入、预期输出和实际输出。 您引用了另一个问题,但没有看到 fread() 返回读取的 items 数量。因此,如果您交换 sizecount 参数,则会得到不同的结果(除非 fsize == 1)。 啊,你基本上回答了我问题的第二部分,谢谢! 【参考方案1】:

错误是未终止的字符串。您为终止字符留出了空间

if( !(plain_text = malloc(plain_text_len + 1)) )  // (simplified)

但是在你设置之后,例如

plain_text[i] += 'A';

你需要用

结束字符串
plain_text[i+1] = '\0';

或当字符串完成时。

对于第二部分,您引用了另一个问题,但没有看到 fread() 返回读取的项目数。因此,如果您交换它的 sizecount 参数,则会得到不同的结果(除非 fsize == 1)。

所以你可以使用

if( fread(fcontents, fsize, 1, file) != 1 ) ...

或者这个

if( fread(fcontents, 1, fsize, file) != fsize ) ...

请注意,我将 sizeof(char) 更改为 1,因为根据定义,它是。

【讨论】:

谢谢,我需要很长时间才能发现这一点! 'sizeof(char)' 位只是为了清楚起见,因为我不太喜欢幻数。

以上是关于C中的维吉尼亚密码的主要内容,如果未能解决你的问题,请参考以下文章

《密码学》维吉尼亚密码。

维吉尼亚密码的Python实现

维吉尼亚密码 C++

密码那些事儿|(十)“钥匙”打开维吉尼亚的锁

维吉尼亚密码:加密强悍,却为何没人用?

维吉尼亚密码(Vigenère cipher)