Beginner programmer - help with authentication problem

Hi folks!


I will be asking forhelp with an assignment and don't expect you guys to do it for me, but if you could help me get on it would be extremely helpful!
I just want to understand!

So basically, the program should, when opening via GitBash:

- Insert Username and Password that are already decided.
- If Username and Password is correct - you're in, otherwise you're not.
- The password is encrypted, so one task is to decrypt the password using ROT7 and ROT9.

We are provided two blocks of code. One main and one definition.
The main is complete (I think, no need to change?) and the definition is supposed to be completed by me.

If I understand this correctly "value" will be printed as "input,input" and I need to separate this string to individual strings.

But how should I think when I actually have two strings? I just can't get my head around it.

Hope someone can nudge me in the right direction! And as I said, not trying to get you to do my homework for me, I just need more than Lippmans C++ primer to understand I guess..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "Prototypes.h"
using std::string;
using std::cout;
using std::endl;

int main(int argc, char* argv[]) {
    string value = mainArgumentParser(argc, argv);
    if (value == "fail")
        cout << "Not enough arguments." << endl;
    else if(authenticateUser(value))
        cout << "Authentication successful!" << endl;
    else
        cout << "Authentication failed." << endl;
    return 0;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "Prototypes.h"
using std::string;
using std::stringstream;
using std::cout;
using std::endl;

string mainArgumentParser(int argc, char* argv[]) {
    if (argc < 3)
        return "fail";
    stringstream ss;
    ss << argv[1] << "," << argv[2];
    return ss.str();
}

bool authenticateUser(string value) {
    const string USERNAME = "Kalle";
    const string PASSWORD = "i0J0u0j0u0J0Zys0r0{";
    bool authPassed = false;

    return authPassed;
}

string decryptPassword(string pass) {
    const int ROT7 = 7, ROT9 = 9;
    const string VOWELS = "AEIOUYaeiouy";
    string decrypted;
    
    return decrypted;

}
Last edited on
If I understand this correctly "value" will be printed as "input,input" and I need to separate this string to individual strings.
1
2
3
   stringstream ss;
    ss << argv[1] << "," << argv[2];
    return ss.str();

First, let me just say that using a stringstream here is kinda overkill.
You could just do:
string str = string(argv[1]) + "," + string(argv[2]);
It's converting the char* c-style strings into std::strings, and then concatenating them.
But anyway, that's a minor note... not really important.

If I understand this correctly "value" will be printed as "input,input" and I need to separate this string to individual strings.
You can do a combination of substr (substring) and find methods that strings provide to separate the two.
Alternatively, you can convert the string into a stringstream (for real this time) and then use getline on it, using ',' as the delimiter.

But how should I think when I actually have two strings? I just can't get my head around it.
I don't know what you mean by "how to think".

I think you're artificially making this more complicated than it needs to be. Is it a requirement you must have a "mainArgumentParser" which returns the combined username,password string? The first argument can be the username, and the second argument can be the password.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string>

int main(int argc, char* argv[])
{
    using namespace std;
 
    if (argc < 3) {
        cout << "Not enough arguments.\n";
        return 1;
    }
    string username = argv[1];
    string password = argv[2];

    // etc.
}

You can then pass the username and password into the other functions as needed.

In your authenticateUser function, you should probably call decryptPassword on the stored password. Then, you compare the decrypted password to the password the user entered.

I don't know what "decrypt using rot7 and rot9" means. I know what rot* means, and I assume you do too, but I'm not sure what the point of combining them is. Isn't rot7 + rot9 just rot16?
Regardless, I would suggest writing a function called
char rot(char letter, int n) or similar, where, for example, rot('a', 1) will return 'b', rot('A', 1) will return 'B', rot('z', 2) will wrap around to return 'b', etc.
Last edited on
The code in my first post is the code we got, I didn't write that. What we need to do is:

Split the string "username,password" so it becomes "username" and "password". Then validate that the username and password is correct.

Splitting the first string and storing the new ones in strings should be pretty straight forward I guess. But then my problem is actually doing anything with the new strings. The new strings should be something like

1
2
3
4
5
if (newstring1 == USERNAME && newstring2 == PASSWORD)
{
Lead to the "else if" in main
}


Sorry if I'm not clear, this is only month two with no prior experience in coding, so still SUPER noob!
Last edited on
It's okay if you're not clear, it takes experience to know how to ask the right questions.

But then my problem is actually doing anything with the new strings.

Yes, once you have the username and password separated, comparing the username with the stored username is straightforward, just do string1 == string2.

The harder part is dealing with the password. I assume that your password, the i0J... string, is stored in the "encrypted" form. So you need to pass this string into your decrypt function before you compare it to the user's string.

To split the two strings by the comma, again I suggest either using stringstream, similar to how it's already being used, or by using a combination of string.find() and string.substr().

http://www.cplusplus.com/reference/string/string/find/
http://www.cplusplus.com/reference/string/string/substr/
Last edited on
I don't know if I missed a class or five but.. we jumped from writing "Hello World" and doing palindrome programs to this... I feel overwhelmed..

I get that it's possible to split the string by the comma and I guess that the string I should be splitting is "value". That far I think I understand!

How to pass the code down to the decrypt function, that I also have to write, I have no idea.

Will it be this frustrating all the three years of uni?
Last edited on
So, I've gotten this far:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include "Prototypes.h"
using std::string;
using std::stringstream;
using std::cout;
using std::endl;

string mainArgumentParser(int argc, char* argv[]) {
    if (argc < 3)
        return "fail";
    stringstream ss;
    ss << argv[1] << "," << argv[2];
    return ss.str();
}

bool authenticateUser(string value)
{
    const string USERNAME = "Kalle";
    const string PASSWORD = "i0J0u0j0u0J0Zys0r0{";
    bool authPassed = false;

    string username = value.substr(0,5);
    size_t pos = value.find(",");
    string passw = value.substr(pos +1);

    if (username == USERNAME && passw == PASSWORD)
    {
        authPassed = true;
    }

    return authPassed;

}

string decryptPassword(string pass)
{
    const int ROT7 = 7, ROT9 = 9;
    const string VOWELS = "AEIOUYaeiouy";
    string decrypted;


    return decrypted;

}


It seems to work fine if I type in the "i0J"-password. Anything I messed up?

Possible to unlock a hint from a C++ grandmaster for how to proceed to make the decryption tool?

i0J0u0j0u0J0Zys0r0{ should translate into bAnanASplit, so for the program to work it should be (in GitBash)

$./Program.exe Kalle bAnanASplit.
-> Authentication successful

Since I don't want to type in the weird, encrypted password I need to turn bAnanASplit into i0J0u0j0u0J0Zys0r0{ by calling the decryptPassword-function somehow?

Ah well, some progress!

we jumped from writing "Hello World" and doing palindrome programs to this... I feel overwhelmed..
Yes, that does seem a bit drastic.

Good progress with using find/substr, that is very similar to how I was envisioning it.
One note is that your are doing substr(0,5), so you are essentially hardcoding the username to be 5 characters long. Not a big deal if the flexibility is not required, but the result of value,find(",") could be used to determine how big the first (username) string is.

Anyway, so yes you need to pass things to functions.
http://www.cplusplus.com/doc/tutorial/functions/

Let's assume you have correctly separated the username from the password.
Your decryptPassword function takes in a string, right?
So, from your authenticateUser function, you need to pass the password to the decryptPassword function.

This is done by something like:
1
2
3
4
    if (username == USERNAME && passw == decryptPassword(PASSWORD))
    {
        authPassed = true;
    }


But now the hard part is actually looking at the encrypted password string, and decrypting it according to your formula.

I actually don't understand right off the bat what it is your decryption ought to it.
You begin with: i0J0u0j0u0J0Zys0r0
I notice the number of 0s in this is equal to the number of lowercase letters in the plaintext password. Now that might just be a coincidence, so don't go too strongly there.
I noticed you've been given a string of vowels to work with. I assume this is related to how the encryption works.

So yeah, what you need to do is explain, step by step, how the decryption process is supposed to work. What do those letters and numbers me? How do they relate to the vowel string?

For example, let's start simpler:
If I have a string "a", what should it encrypt to?
And given that encrypted string, what do I need to do to transform it back into "a"?

_____________________________________________

I also noticed that "J0" maps to A both times, if you break up the encrypted string.
1
2
i0 J0 u0 j0 u0 J0
b  A  n  a  n  A 

Also, j0 (lowercase J) makes to lower-case a.

"J" is the 10th letter of the alphabet, and "A" is the 1st letter of the alphabet. So the decryption is shifting the J backwards by (10 - 1, = 9) letters.

The "u0" decrypts to n both times.
U = 21, N = 14
U - N = 7.
The the u0 is being decrypted by a shift of 7.

Your cipher seems to be decrypted by alternating shifts of 9 and 7. At least part of it?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Example program
#include <iostream>
#include <string>

using std::string;
using std::cout;

void test_decrypt()
{
    string password = "i0J0u0j0u0J0Zys0r0{";   
    
    string decrypted_password;
    for (size_t i = 0; i < password.length(); i+=2)
    {
        if (i % 4 == 0)
        {
            decrypted_password += password[i] - 7;
        }
        else
        {
            decrypted_password += password[i] - 9;   
        }
    }
    cout << decrypted_password << '\n';    
}

int main()
{
    test_decrypt();
}

Output:
bAnanASjkr

Clearly, the remaining part of the password doesn't match this pattern, that's where my pattern breaks down. The 0 (and lack of 0 in the later part of the string) must mean something I; assume it's another type of shift.
Last edited on
Oh wow, the tutorials on here is so much better than the course litterature we've been given!

We actually got the tools to decrypt, sorry for not sharing right away to save you from trying to figure it out:

- Two rotation keys (ROT 7 and 9). They alternate as we traverse through the string. Even traversal = ROT 7, uneven traversal = ROT9.

- If value of undecrypted character in password is a vowel, add 0 both in front and behind.


Something like this roughly?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
string decryptPassword(string pass)
{
    const int ROT7 = 7, ROT9 = 9;
    const string VOWELS = "AEIOUYaeiouy";
    string decrypted;
    string O = "0""0";
    pass = "bAnanASplit";
    
    *PSEUDOCODE*
    if traversal even, +7
    if traversal uneven, +9
    if vowel, add "0" "0"

    return decrypted;

}


Thanks a lot for all the help. Really making everything much clearer. Panic is almost all gone, haha!
Ok, I'm getting nowhere..

I tried just the adding of "0", but this output is no bueno:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
string decryptPassword(string pass)
{
    const int ROT7 = 7, ROT9 = 9;
    const string VOWELS = "AEIOUYaeiouy";
    string decrypted;
    pass = "bAnanASplit";

    auto spacePos = pass.find(VOWELS[1-12]);
    if (spacePos != string::npos)
        pass.insert(0 & 2, 0);

   return decrypted;

}


Panic is back!

Should the zeroes be added before it's encrypted? Wouldn't that make the traversal step harder? Sigh..
Last edited on
Your function is called decryptPassword. That means that the input to the function should be the encrypted password, presumably. So, are you trying to encrypt the password, or decrypt it? I'm not sure what you want, so I'm just going off what the name of your function says: decrypt password.

Currently, you are passing in some string as the input, but then re-assigning this string on line 6. This defeats the purpose of passing in the string.

You are also trying to access VOWELS[-11] on line 8, that's what the 1-12 is doing, you know. That's not how you specify ranges. Also, "0 & 2" is doing bitwise logic on 0b00 and 0b10, which just evaluates to 0, so I don't think that's what you want to do, either. So I guess just scratch that stuff. I'll show you how to properly check if a number is even below.

Start with this, just focusing on this one function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Example program
#include <iostream>
#include <string>

using std::string;
using std::cout;

string decryptPassword(string pass)
{
    const int ROT7 = 7, ROT9 = 9;
    const string VOWELS = "AEIOUYaeiouy";

    string decrypted; // starts as empty string
    
    // fill in decrypted string based on encrypted string
    decrypted = "blah"; // TODO
    
    return decrypted;
}

int main()
{
    string encrypted_password = "i0J0u0j0u0J0Zys0r0{"; 
    string decrypted_password = decryptPassword(encrypted_password);   
    
    cout << decrypted_password << '\n';
}


If value of undecrypted character in password is a vowel, add 0 both in front and behind.
Undecrypted? :) I assume that means "plaintext", or "unencrypted".
These instructions are describing how something is encrypted, but currently you (presumably) are trying to decrypt the secret, stored password.

This is actually kinda funny to me, because if I'm understand the directions correctly, the prompt still seems under-specified to me. What happens if a character actually encrypts to '0'? Should a run of three 0's in a row be treated differently in some way?

If I ignore the special case of an actual '0', it appears to me that adding '0' to the encrypted string doesn't actually add any information. So... it would seem, you can simply remove the 0s from the encrypted string.

i.e. receive the encrypted string, make a new string but only add the non-0 characters to it. Then, do the "ROT" processing on this string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Example program
#include <iostream>
#include <string>

using std::string;
using std::cout;

// % is the modulo operator,
// it's (pretty much) how 'remainder' works in math
// this is saying "is the remainder 0 when n is divided by 2"
bool isEven(int n)
{
    return n % 2 == 0;   
}

string decryptPassword(string pass)
{
    string sanitized_pass; // starts as empty string
    
    // remove 0s (copy over non-'0' characters)
    for (size_t i = 0; i < pass.length(); i++)
    {
        if (pass[i] != '0')
            sanitized_pass += pass[i];
    }
    
    string decrypted;
    
    // do the shifting
    for (size_t i = 0; i < sanitized_pass.length(); i++)
    {
        if (isEven(i))
        {
            // TODO
            // shift by 7, append (concatenate) result to decrypted
        }
        else
        {
            // TODO
            // shift by 9, append (concatenate) result to decrypted
        }
    }
    
    return decrypted;
}

int main()
{
    string encrypted_password = "i0J0u0j0u0J0Zys0r0{"; 
    string decrypted_password = decryptPassword(encrypted_password);   
    
    cout << decrypted_password << '\n';
}
Last edited on
Not native english speaker, so apologies!

It actually says "- If the undecrypted character value is a vowel, it should be both preceeded and succeeded by 0." in the assignment instructions. School is known for being unclear tho..

Basically what they want is to insert:
"Kalle bAnanASplit"

and get to

 
authPassed = true;


So, after I split the string from username,password, password should go to decryptPassword and go from bAnanASplit to "i0J0u0j0u0J0Zys0r0{"

so:

1
2
b + ROT7 -> i
A -> add zeroes -> 0A0 -> +ROT9 -> 0J0

etc etc.

Just woke up and ready to get on it again, will study the code you wrote and see if I understand it!
___________________________________________________

I understand it as follows:

Here, the "passw" is the password entered by the user, i.e bAnanASplit (if we want it to be successful)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool authenticateUser(string value)
{
    const string USERNAME = "Kalle";
    const string PASSWORD = "i0J0u0j0u0J0Zys0r0{";
    bool authPassed = false;

    string username = value.substr(0,5);
    size_t pos = value.find(",");
    string passw = value.substr(pos +1);

    if (username == USERNAME && decryptPassword(passw) == PASSWORD)
    {
        authPassed = true;
    }

    return authPassed;

}


passw (bAnanASplit) is passed on to the function "decryptPassword" where it should be altered to be == to PASSWORD in the code above ( i0J0u0j0u0J0Zys0r0{ )

If my thinking is right your code is doing the opposite? So it alters the other way around, right? So with a bit of sweat and tears I SHOULD be able to get what I want with the help of the code you provided?
Last edited on
I solved it!

It doesn't look pretty, and I'd love another solution for getting the zeroes in there but... it's something!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include "Prototypes.h"
#include <iostream>
using std::string;
using std::stringstream;
using std::cout;
using std::endl;

string mainArgumentParser(int argc, char* argv[]) {
    if (argc < 3)
        return "fail";
    stringstream ss;
    ss << argv[1] << "," << argv[2];
    return ss.str();
}

bool authenticateUser(string value)
{
    const string USERNAME = "Kalle";
    const string PASSWORD = "i0J0u0j0u0J0Zys0r0{";
    bool authPassed = false;

    string username = value.substr(0,5);
    size_t pos = value.find(",");
    string pass = value.substr(pos +1);

    if (username == USERNAME && decryptPassword(pass) == PASSWORD)
    {
        authPassed = true;
    }
    return authPassed;
}

bool even(int n)
{
    return n % 2 == 0;
}

string decryptPassword(string pass)
{
    const int ROT7 = 7, ROT9 = 9;
    const string VOWELS = "AEIOUYaeiouy";
    string decrypted;
    const string zero = "0";

    for (size_t i = 0; i < pass.length(); i++)
        if (even(i))
        {
           pass[i] += ROT7;
           decrypted = pass;
        } else
        {
            pass[i] += ROT9;
            decrypted = pass;
        }

    decrypted.insert(1, zero);
    decrypted.insert(3, zero);
    decrypted.insert(5, zero);
    decrypted.insert(7, zero);
    decrypted.insert(9, zero);
    decrypted.insert(11, zero);
    decrypted.insert(15, zero);
    decrypted.insert(17, zero);

    return decrypted;

}


How would one use the VOWEL-string to get the zeroes in the right place? I feel like they want you to use it since they put it there!
Last edited on
It all depends on what the actual requirements of the assignment are. How flexible it needs to be. I'm not your professor so I don't know.

It may work, but it only will work for your current hard-coded "PASSWORD". If this is acceptable, then I guess it's okay.

But also -- this goes back to my previous point -- you are currently encrypting the password, not decrypting it. So the name of your function is misleading, it should be called "encryptPassword(string pass)", because that's what you're doing. Yes, it's the opposite of what I was doing.

"decrypt" means to turn back into plaintext (i.e. what the user actually types).
"encrypt" is obfuscating the password and adding those zeroes and what not.

i.e. in an ideal situation, this would be true for any input:
encryptPassword(plaintext_password) == encrypted_password
decryptPassword(encrypted_password) == plaintext_password

If you want it to be more flexible, and actually use the VOWELS string, then you should only be inserting '0's if the current character is a vowel.
e.g.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Example program
#include <iostream>
#include <string>

int main()
{
    using std::string;
    using std::cout;
    
    const string Vowels = "AEIOUYaeiouy";
    
    string text = "QuickBrownFoxLazy";
    
    string text_with_zeroes_around_vowels;
    
    for (size_t i = 0; i < text.length(); i++)
    {
        if (Vowels.find(text[i]) != string::npos)
        {
            // text[i] is vowel
            
            if (i > 0 && text_with_zeroes_around_vowels.back() != '0')
            {
                // add a zero to the left if it doesn't already exist
                text_with_zeroes_around_vowels += '0';
            }
            // add the actual character and a zero to the right
            text_with_zeroes_around_vowels += text[i];
            text_with_zeroes_around_vowels += '0';
        }
        else
        {
            // text[i] is not vowel
            text_with_zeroes_around_vowels += text[i];
        }
    }

    cout << text_with_zeroes_around_vowels << '\n';
}

Q0u0i0ckBr0o0wnF0o0xL0a0z0y0

Last edited on
The name of the function was chosen by our teacher, and we are not allowed to alter any of the original code.

Of course hard for you to know since you haven't seen the original which is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "Prototypes.h"
using std::string;
using std::stringstream;
using std::cout;
using std::endl;

string mainArgumentParser(int argc, char* argv[]) {
    if (argc < 3)
        return "fail";
    stringstream ss;
    ss << argv[1] << "," << argv[2];
    return ss.str();
}

bool authenticateUser(string value) {
    const string USERNAME = "Kalle";
    const string PASSWORD = "i0J0u0j0u0J0Zys0r0{";
    bool authPassed = false;
    /**
     * Add needed code as stated in the lab description
     */

    return authPassed;
}

string decryptPassword(string pass) {
    const int ROT7 = 7, ROT9 = 9;
    const string VOWELS = "AEIOUYaeiouy";
    string decrypted;
    /**
     * Add needed code as stated in the lab description
     */
    return decrypted;

}


The assignment is to turn bAnanASplit into the hardcoded password, so my solution should be acceptable! But I think I'll learn more if I actually do it like you did it above.

My concern is:

If I would put in the zeroes before using the rotation it would be messed up, right?

bAnanASplit

After zeroes added: b0A0n0a0n0A0Spl0i0t

So if "pass" is b0A0n0a0n0A0Spl0i0t instead of bAnanASplit:

1
2
3
4
5
6
7
8
9
10
11
for (size_t i = 0; i < pass.length(); i++)
        if (even(i))
        {
           pass[i] += ROT7;
           decrypted = pass;
        } else
        {
            pass[i] += ROT9;
            decrypted = pass;
        }


Output would be:

i090H090u090h9u090H9Zys9p9{


There is obviously some smart way around that as well..
Topic archived. No new replies allowed.