首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Vigenere密码C程序

Vigenere密码C程序
EN

Code Review用户
提问于 2020-11-23 18:13:12
回答 3查看 1.5K关注 0票数 7

我实现了一个Vigenere密码,它保留大小写,如果传递-d参数,也可以解密。使用是./vigenere <plaintext|ciphertext> key [-d],它工作得很好,但我认为可能有一些事情我可以做得更好。所以,我只是在找批评。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define ENCRYPT 1
#define DECRYPT -1

int mod(int x, int n)
{
    return (x % n + n) % n;
}

char *vigenere(char *input, char *keystream, int mode)
{
    if (strlen(keystream) != strlen(input))
        return NULL;
    
    int output_len = strlen(input), offset = 'a', wasupper = 0; 
    char *output = malloc(output_len);
    
    for (int i = 0; i < output_len; ++i)
    {
        if (isupper(input[i]))
        {
            input[i] = tolower(input[i]);
            wasupper = 1;
        }
        else
            wasupper = 0;
            
        output[i] = mod((input[i] - offset) + (keystream[i] - offset) * mode, 26) + offset;
        
        if (wasupper)
            output[i] = toupper(output[i]);
    }

    return output;
}

char *gen_keystream(char *key, int keystream_len)
{
    int key_len = strlen(key), j = 0;
    char *keystream = malloc(keystream_len);
    
    for (int i = 0; i < keystream_len; j = ++i % key_len)
        keystream[i] = key[j];
        
    return keystream;
}

int main(int argc, char **argv)
{
    if (argc < 3)
        return EXIT_FAILURE;
        
    int mode = ENCRYPT;
        
    if (argc > 3 && strcmp(argv[3], "-d") == 0)
        mode = DECRYPT;
        
    int keystream_len = strlen(argv[1]);
    char *keystream = gen_keystream(argv[2], keystream_len);
    char *result = vigenere(argv[1], keystream, mode);
    
    printf("%s\n", result);

    free(keystream);
    free(result);
    
    return EXIT_SUCCESS;
}
EN

回答 3

Code Review用户

发布于 2020-11-23 19:18:13

处理数据流,而不是单个字符串

程序的工作方式是将输入作为命令行参数传递,但是如果要对整个文件进行编码或解码怎么办?如果输入很大怎么办?我将尝试修改程序,以便它可以从文件中运行,或者在没有指定文件的情况下从标准输入运行,因此可以这样做:

代码语言:javascript
复制
./vigenere key < plaintext.txt > ciphertext.txt
票数 8
EN

Code Review用户

发布于 2020-11-23 19:55:11

以下是一些可以帮助您改进代码的东西。

总是使用大括号

目前的守则包括:

代码语言:javascript
复制
if (isupper(input[i]))
{
    input[i] = tolower(input[i]);
    wasupper = 1;
}
else
    wasupper = 0;

    output[i] = mod((input[i] - offset) + (keystream[i] - offset) * mode, 26) + offset;
    
    if (wasupper)
        output[i] = toupper(output[i]);

问题是,else后面的缩进表明,只有当isupper(input[i])计算为false时才执行所有这些行,但情况并非如此。只有wasupper = 0;行在该路径上被独占执行。因此,代码的读者必须试图找出它是缩进错误还是缺少大括号错误。出于这个原因,特别是如果您对该语言还不熟悉,我建议对此类条件结构始终使用{}

修复bug

C中的字符串必须以'\0'字符终止,才能使用函数调用(如strlen() ),但gen_keystreamvigenere中存在错误,无法解释终止符。请记住,strlen()返回不包括此终止字符的字符串的长度。

仔细思考数学运算

这个函数并没有中断,但是它并没有达到它所能达到的效率:

代码语言:javascript
复制
int mod(int x, int n)
{
    return (x % n + n) % n;
}

我们可以使用简单的return (x + n) % n;来表达相同的概念,考虑到x的预期范围是(-n,n)。您还可以简化一点以上的内容,我将在后面演示。

在实际的

中使用const

因为vigenere不应该改变inputkeystream,所以两者都应该声明为const

将每条语句放在单行

这不利于代码的可读性,在同一行中声明多个变量,或者滥用逗号运算符:

代码语言:javascript
复制
int output_len = strlen(input), offset = 'a', wasupper = 0; 

相反,在自己的行中分离每个语句可以使代码更容易阅读和维护。请参阅ES.10

检查错误的返回值

malloc的调用可能失败。您必须检查返回值,以确保它们没有,或者当给定错误的输入或由于系统资源不足时,您的程序可能崩溃(或更糟)。严格的错误处理是大多数工作软件和无错误软件之间的区别。你应该为后者而奋斗。

考虑是否需要

函数

还有其他方法来构造程序,这样就不需要gen_keystreamkeystream了。您可以简单地将密钥传递到修改过的vigenere版本中。

考虑使用指针而不是索引

对每个C程序员来说,有效地使用指针是一项基本技能。下面是您的函数的重写版本,使用了上面提到的大多数简化和更正:

代码语言:javascript
复制
char *vigenere(const char *input, const char *key, int mode)
{
    int output_len = strlen(input); 
    if (output_len == 0) {
        return NULL;
    }
    // add +1 for the terminating NUL character
    char *output = malloc(output_len + 1);
    if (output == NULL) {
        return output;
    }
    const char *keyptr = key;
    output[output_len] = '\0';
    
    for (char *curr = output ; *input; ++input) {
        if (*keyptr == '\0') {
            keyptr = key;
        }
        *curr = (mode * (*keyptr - tolower(*input)) + 26) % 26 + 'a';
        if (isupper(*input)) {
            *curr = toupper(*curr);
        }
        ++curr;
        ++keyptr;
    }
    return output;
}

注意,在这段代码中,我假设已经检查了所有字符,对于所有输入,isalpha()都是真的,对于键中的每个字符,islower()isalpha()都是真的。

票数 6
EN

Code Review用户

发布于 2020-11-24 17:10:51

这不是我的密码

这一行:

if (strlen(keystream) != strlen(输入))返回NULL;

意味着键需要与输入完全相同的长度。所以你真正拥有的是一个一次性的垫子。

相反,一个真正的Vignère密码可以通过重用每个strlen(key)输入字符的键来编码任意大的消息。这解释了为什么Vignère密码不像OTP那么安全,特别是对于短密钥,但是它更有用。

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

https://codereview.stackexchange.com/questions/252546

复制
相关文章

相似问题

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