首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Vigenere密码破碎机

Vigenere密码破碎机
EN

Code Review用户
提问于 2016-11-28 13:47:23
回答 2查看 1.1K关注 0票数 6

我是C++的新手。我最近完成了一个命令行程序,它通过测试字典中的每个单词从文件中破解Vigenere编码的消息。

我从来没有C++的家教/老师,所以我想确保我没有养成任何坏习惯。

Main.cpp:

代码语言:javascript
复制
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include "Alphanumeric.h" //turns characters into numerical equivalents
#include "Vigenere.h" //Vigenere Functions
#include <list>

#ifdef __APPLE__
    #define DICTPATH "/usr/share/dict/words"
#endif


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

std::cout << "*************VIGENERE BREAKER*************" << std::endl;

std::ifstream input(argv[1]);
std::ifstream dictionary(DICTPATH);

std::string process = argv[2];
std::string key = argv[3];

std::string plain;
std::string str;

while (std::getline(input, str))
{
    plain += str;
}

if (process == "-e")
{
    std::cout << "*************ENCODE*************" << std::endl;
    std::string result = Vigenere::EncryptVigenere(plain, key);
    std::cout << result << std::endl;
}
if (process =="-d")
{
    std::cout << "*************DECODE*************" << std::endl;
    std::string result = Vigenere::DecryptVigenere(plain, key);
    std::cout << result << std::endl;
}

if (process == "-dicf")
{
    const float englishfreq[] = {8.2,1.5,2.8,4.3,12.7,2.2,2,6.1,7,0.2,0.8,4,2.4,6.7,7.5,1.9,0.1,6,6.3,9.1,2.7,1,2.4,0.15,1.9,0.074};
    const int threshold = atoi(argv[3]);

    std::cout << "*************DICTIONARY_ATTACK*************" << std::endl;
    std::list<std::string> results;
    while (std::getline(dictionary, str))
    {
        float total = 0;
        float frequency[26] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

        std::cout << str << std::endl;
        std::string result = Vigenere::DecryptVigenere(plain, str);


        for (char c : result)
        {
            frequency[Alphanumeric::chartoint(c)]++;
        }

        int size = result.size();
        int index = 0;
        for (float freq : frequency)
        {
            freq = (freq / size)*100;
            float difference = freq - englishfreq[index];
            if (difference < 0)
            {
                difference *= -1;
            }
            total += difference;
            index++;
        }

        if (total <= threshold) {
            std::cout << "RESULT FOUND" << std::endl;
            results.push_back("Using " + str);
            results.push_back(result);
        }
    }
    std::cout << "*************RESULTS*************" << std::endl;
    for (std::string s : results)
    {
        std::cout << s << std::endl;
    }
    std::cout << "dictionary attack complete!!!" << std::endl;
}
if (process == "-dic")
    {

        std::cout << "*************DICTIONARY_ATTACK*************" << std::endl;
        std::list<std::string> results;
        std::string keyword = argv[3];
        while (std::getline(dictionary, str))
        {
            std::cout << "key:";
            std::cout << str << std::endl;
            std::string result = Vigenere::DecryptVigenere(plain, str);

            if (result.find(keyword) != std::string::npos) {
                std::cout << "RESULT FOUND" << std::endl;
                results.push_back("Using " + str);
                results.push_back(result);
            }
        }
        std::cout << "*************RESULTS*************" << std::endl;
        for (std::string s : results)
        {
            std::cout << s << std::endl;
        }
        std::cout << "dictionary attack complete!!!" << std::endl;
    }
return 0;
}

Vigenere.h:

代码语言:javascript
复制
#ifndef VIGENERE_H_
#define VIGENERE_H_

namespace Vigenere
{

std::string DecryptVigenere(std::string ciphertext, std::string key);
std::string EncryptVigenere(std::string plaintext, std::string key);

}
#endif /* VIGENERE_H_ */

Vigenere.cpp:

代码语言:javascript
复制
#include <string>
#include "Vigenere.h"
#include "Alphanumeric.h"

std::string Vigenere::DecryptVigenere(std::string ciphertext, std::string key)
    {
    std::string answer;
    for (int i = 0; i < ciphertext.length(); i++)
    {
        int numeric = Alphanumeric::chartoint(ciphertext[i]);
        int keyval = Alphanumeric::chartoint(key[i%key.length()]);
        numeric -= keyval;
        if (numeric < 0)
        {
            numeric += 26;
        }
        numeric %= 26;
        answer += Alphanumeric::inttochar(numeric);
    }
    return answer;
}

std::string Vigenere::EncryptVigenere(std::string plaintext, std::string key)
{
    std::string answer;

    for (int i = 0; i < plaintext.length(); i++)
    {
        int numeric = Alphanumeric::chartoint(plaintext[i]);
        int keyval = Alphanumeric::chartoint(key[i % key.length()]);
        numeric += keyval;
        numeric %= 26;
        answer += Alphanumeric::inttochar(numeric);
    }
    return answer;
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2016-11-29 08:28:16

代码语言:javascript
复制
std::string DecryptVigenere(std::string ciphertext, std::string key);
std::string EncryptVigenere(std::string plaintext, std::string key);

对于输入参数,通过引用到-const传递非原语类型,以避免不必要的分配。见康斯特-正确性参数传递

代码语言:javascript
复制
std::string DecryptVigenere(std::string const& ciphertext,
                            std::string const& key);
std::string EncryptVigenere(std::string const& plaintext,
                            std::string const& key);
代码语言:javascript
复制
#include <string>
#include "Vigenere.h"
#include "Alphanumeric.h"

通过健壮性组织您的头,以便尽快捕获潜在使用错误

代码语言:javascript
复制
#include "Vigenere.h"      // Interface
#include "Alphanumeric.h"  // Project-Level header
#include <string>          // Language-Level header
代码语言:javascript
复制
std::string EncryptVigenere(std::string const& plaintext,
                            std::string const& key);
{
    std::string answer;

当您知道容器所需的空间大小时,请使用reserve成员函数预先分配空间。在两个转换函数中,答案的长度总是与第一个输入参数的长度相同。

代码语言:javascript
复制
std::string EncryptVigenere(std::string const& plaintext,
                            std::string const& key);
{
    std::string answer;
    answer.reserve(plaintext.size());
代码语言:javascript
复制
        int keyval = Alphanumeric::chartoint(key[i%key.length()]);

确保测试你的先决条件。key必须是非空的,因为您不能将/模块化除以零(根据C++标准§5.6/4,未定义的行为)。

与格式一致(操作符间距、函数命名)。

代码语言:javascript
复制
std::string encrypt_vigenere(std::string const& plaintext,
                            std::string const& key);
{
    if (key.empty()) {
        throw std::invalid_argument("Invalid key length of Zero.");
    }
    // ...
        int keyval = Alphanumeric::char_to_int(key[i % key.length()]);
代码语言:javascript
复制
    std::cout << "*************VIGENERE BREAKER*************" << std::endl;

要知道std::endl实际上做了什么。

将换行符插入到输出序列os中,并像调用os.put(os.widen('\n'))os.flush()一样刷新它。

如果您不打算强制缓冲区刷新,请考虑只输出'\n'

代码语言:javascript
复制
    std::cout << "*************VIGENERE BREAKER*************\n";
代码语言:javascript
复制
    std::ifstream input(argv[1]);
    std::string process = argv[2];
    std::string key = argv[3];

检查以确保这些参数确实存在。超出界限的索引访问是未定义的行为。

const限定了不可变对象。

代码语言:javascript
复制
    if (argc != 4) {
        // TODO: Print a helpful message instead.
        std::cerr << "The syntax of the command is incorrect.\n";
        return EXIT_FAILURE;
    }

    std::ifstream input(argv[1]);
    const std::string process = argv[2];
    const std::string key = argv[3];
代码语言:javascript
复制
#ifdef __APPLE__
    #define DICTPATH "/usr/share/dict/words"
#endif

// ...
    std::ifstream dictionary(DICTPATH);

如果没有定义__APPLE__怎么办?如果有备用字典就好了。

不要使用宏来定义变量。如果需要对变量、函数或对象进行编译时评估,请使用constexpr。源代码管理仍然是宏的合法用例。

代码语言:javascript
复制
#ifdef __APPLE__
constexpr auto dictionary_path = "/usr/share/dict/words";
#else
constexpr auto dictionary_path = "english.txt"
#endif

// ...
    std::ifstream dictionary(DICTPATH);
代码语言:javascript
复制
    if (process == "-e") { /* ... */ }
    if (process == "-d") { /* ... */ }
    if (process == "-dicf") { /* ... */ }
    if (process == "-dic") { /* ... */ }

process可以支路的块是相互排斥的。要强制执行这种排他性,您可以使用else if向读者表明正在发生这种情况。

如果进程无效,应该发生什么?

代码语言:javascript
复制
    if (process == "-e") { /* ... */ }
    else if (process == "-d") { /* ... */ }
    else if (process == "-dicf") { /* ... */ }
    else if (process == "-dic") { /* ... */ }
    else { /* handle invalid process */ }
代码语言:javascript
复制
            frequency[Alphanumeric::chartoint(c)]++;

这在我看来有点可疑。我希望Alphanumeric类或函数集合返回一组字符和数字的映射。26个角色看上去不太对。

代码语言:javascript
复制
        if (total <= threshold) {
            std::cout << "RESULT FOUND" << std::endl;
            results.push_back("Using " + str);
            results.push_back(result);
        }

因为结果没有保存在任何东西上,所以当你找到结果时,就打印出来。

票数 3
EN

Code Review用户

发布于 2016-11-28 14:33:12

码设计

如果您是C++的新手,这是非常好的,但是我认为您可以用C++做的最糟糕的事情之一是像C一样使用它。如果您不使用面向对象的范例,那么您更有可能使用STL编写C代码,而不是实际的C++。

如果您需要这个练习的第二个版本的想法,您可以尝试以一种将代码从主要功能中提取出来的方式来设计它,以便在课堂上组织它。这还允许您轻松地存储和使用您复制并粘贴到多个地方的神奇数字"26“(这是一个非常糟糕的实践,因为它会损害可维护性)。

错误检查

检查命令行参数错误并给出反馈可能是个好主意。您应该检查argv[]的长度以避免任何问题,但是如果用户的参数不符合您的期望,您也应该向他提供反馈。

容器

您正在使用std::list来存储结果,但您似乎只对其使用了push_backstd::vector将是一个更适合您使用的容器。

迭代

我也喜欢foreach循环,但是当您需要跟踪索引时,这可能意味着您应该使用一个标准的for循环。这就是在频率数组上迭代时所做的事情。

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

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

复制
相关文章

相似问题

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