1
\$\begingroup\$

I'm studying algorithm's and got to the point of hashing.I was excited about it and I created a little hashing function which will depend on user input and generate passwords. The passwords had to look completely random, depend largely on input and be consistent(same input - same pass generated), however not looking similar for similar inputs.I have read someware that function rand() in C++ will generate same sequence of random numbers for same seed. So I used it as a part of algorithm. I'm not sure how safe it is but so far it works well. Any ideas on improving code or algorithm are welcomed!

void User::HashPassword()//Hash pass method of User class
{
    if (IsExisting())//checking if input was valid
    {
        UserHashPassword.clear();
        std::string Combine = UserName + UserPassword + UserURL;
        const unsigned int VALUE = Combine.length();
        for (auto Letter : Combine)
        {
            srand(VALUE*Letter);//seed that depends on user input - new for each letter, and input total length
            UserHashPassword += 33 + rand() % 92;//borders set for ASCII codes that exist on english keyboards
        }
    }

}

\$\endgroup\$
  • \$\begingroup\$ I don't see how "which stores generated pass" and "generate passwords without storing them" may possibly match. \$\endgroup\$ – vnp Oct 27 '17 at 3:41
  • \$\begingroup\$ my bad, by "without storing them" I really meant "without storing them to file/database" \$\endgroup\$ – Max Oct 27 '17 at 3:49
  • \$\begingroup\$ Could you also provide some sample outputs (based on test input values?) \$\endgroup\$ – hjpotter92 Oct 27 '17 at 5:01
  • \$\begingroup\$ Its fun to experiment with this. But the rule here is never write your own hashing (or encryption algorithms). You need PhD level maths skills to get away with that and do it correctly. Look up an existing hashing algorithm and implement that. \$\endgroup\$ – Martin York Oct 27 '17 at 5:55
  • 1
    \$\begingroup\$ Obligatory link to Don't roll your own \$\endgroup\$ – tkausl Oct 27 '17 at 7:04
6
\$\begingroup\$

Structure

Okay, let's start with the basic overall structure of the function: it's, frankly, not very good. It (unnecessarily) depends on a number of outside variables--from its perspective, essentially globals.

There's no good reason that this couldn't be a nice, pure function--i.e., take an input parameter, do the hash, return a result. No reference to anything outside the function at all.

int HashPassword(std::string const &Combine) { 
    unsigned int hash = 0;

    const unsigned int VALUE = Combine.length();
    for (auto Letter : Combine)
    {
        srand(VALUE*Letter);
        hash += 33 + rand() % 92;
    }
    return hash;
}

Naming

Some of the names are pretty good (e.g., Letter, HashPassword) but others (e.g., VALUE, Combine) seem like they're open to some improvement. I can see how you came up with Combine, but that doesn't really make it a good name. VALUE is so generic that it's probably best to avoid it except (perhaps) in something like a class that represents some number, where the VALUE of the object has a fairly obvious meaning.

Magic numbers

The 33 and 92 are a couple of examples of magic numbers. These generally lead to code that's difficult to read (at best).

Security

This hash function provides approximately what's usually referred to as "kid sister" level security. If you have a kid sister, this might be about enough to keep her fooled--but only if she's not very bright or fairly quickly bored.

The most obvious problem is that this just generates a value from each byte, then adds those values together. This means nobody really needs to know your actual password--at most they only need to care about the letters that make up the password. For example, HashPassword("ABC"), and HashPassword("CAB") both produce exactly the same result.

So, to get the right result, we don't really even need to guess their password--we just need to guess some characters that produce the right result--and since nearly every input in the right range maps to a unique result, it doesn't take a lot to come up with a usable result either. For example, let's consider a result of 628. Each input produces a result from 32 to 124, so we might want to use, say, seven characters in a password. So, let's start by computing the result for seven repetitions of each character in the most common range:

for (int i = '0'; i < 'z'; i++) {
    std::string in(7, (char)i);
    std::cout << "in: " << in[0] << ", hash: " << HashPassword(in) / 7 << "\n";
}

So, our smallest result is 32--a little more than the "28" that's left over if we divide 628 by 6, so we want something that produces a result a little less than 100. A quick glance shows that 1 produces a result of 87--that'll probably do. So, if we start with 111111, we should get a result of 6 * 87 = 522. That gives us 106 left over. Another glance through the table shows that "R" gives 106, so an input of 111111R should give us 628. A quick run with std::cout << HashPassword("111111R"); verifies that it does exactly that.

In case it seems like I hand-picked something that would be easy, I'll point out that I actually got the 628 from hashing "Password"--so to find 111111R, I really did start over from scratch--and I didn't even have to work hard at it--my first attempt worked out easily.

For another approach, I could have started with 5 of something. Let's say 'j', which gives 102. 5 x 102 = 510. I want two more characters to make up the remainder, so for my next character I want to pick something fairly small. Let's say _, which produces 35. With the previous 510, we get 545. A quick subtraction shows that we need 83 more, and a quick search shows that "M" produces 83, so jjjjj_M would work as well (and a quick test verifies that yes, it also produces 628).

Bottom line: from a security viewpoint, this is so weak that it's open to question whether it really even qualifies as "kid sister" level, or if it's too weak to even qualify for that level of "security".

\$\endgroup\$
  • \$\begingroup\$ Thank you! My intendent use for this argorithm was the idea of having user enter some name, and master password, than enter desired Url and program would generate password to use on website. This way I can memorize only my master password and program can generate passwords for gmail,yahoo etc. from it. \$\endgroup\$ – Max Oct 27 '17 at 6:50
  • \$\begingroup\$ Just for demonstration:User Name - Adler; Master Password: maxvell; Service: gmail.com. Generated password - qyuaE^cpCauu3^c`ub4/^, \$\endgroup\$ – Max Oct 27 '17 at 6:51
  • \$\begingroup\$ Lets say someone pulls gmail's user database, most likely it will be hard for them to brake this password using brute force method, so they will move on to next user. And I don't have to memorize 20 different passwords for each web-site, just use program to generate it again from same input. So even if they get my credentials for one web-site, it won't work on all of them. \$\endgroup\$ – Max Oct 27 '17 at 6:58
  • \$\begingroup\$ However you opened my eyes on some things that I didn't quite understand, I just get too excited seing words like algorithm, hashing, encryption etc. but I'm just in the begining of this long path. \$\endgroup\$ – Max Oct 27 '17 at 7:00
  • 1
    \$\begingroup\$ I'm glad you're still around. For the last sentence, you could put something like "it is not really security by encryption, it is security by obscurity". \$\endgroup\$ – Incomputable Oct 27 '17 at 10:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.