1

I've tried implementing Vigenere's Cypher. I found out that it is used with Uppercase letters but I've made it to work for capital and small letters but the characters of a plain or cyphered text must be the same as their corresponding ones in the Key. So I've done this:

std::string encryptUpper(std::string const& plain, std::string const& key){
    std::string cyphered;
    for(std::size_t i = 0, j = 0, plainLen = plain.length(), keyLen = key.length();
        i != plainLen; ++i, ++j){
        if(j == keyLen)
            j = 0;
        cyphered += ( (plain.at(i) + key.at(j) ) % 26) + 'A';
    }
    return cyphered;
}

std::string decryptUpper(std::string const& cyphered, std::string const& key){
    std::string plain;
    for(std::size_t i = 0, j = 0, cypheredLen = cyphered.length(), keyLen = key.length();
        i != cypheredLen; ++i, ++j){
        if(j == keyLen)
            j = 0;
        plain += ( ( (cyphered.at(i) - key.at(j) + 26) % 26) + 'A');
    }
    return plain;
}

std::string encryptLower(std::string const& plain, std::string const& key){
    std::string cyphered;
    for(std::size_t i = 0, j = 0, plainLen = plain.length(), keyLen = key.length();
        i != plainLen; ++i, ++j){
        if(j == keyLen)
            j = 0;
        cyphered += ( (plain.at(i) + key.at(j) ) % 26) + 'a';
    }
    return cyphered;
}

std::string decryptLower(std::string const& cyphered, std::string const& key){
    std::string plain;
    for(std::size_t i = 0, j = 0, cypheredLen = cyphered.length(), keyLen = key.length();
        i != cypheredLen; ++i, ++j){
        if(j == keyLen)
            j = 0;
        plain += ( (cyphered.at(i) - key.at(j) + 26 ) % 26) + 'a';
    }
    return plain;
}


std::string encrypt(std::string const& plain, std::string const& key){
    std::string cyphered;
    for(std::size_t i = 0, j = 0, plainLen = plain.length(), keyLen = key.length();
        i != plainLen; ++i, ++j){
        if(j == keyLen)
            j = 0;
        cyphered += ( (plain.at(i) - (std::isupper(plain.at(i)) ? 'A' : 'a') + key.at(j) - (std::isupper(plain.at(i)) ? 'A' : 'a') ) % 26) +
        (std::isupper(plain.at(0)) ? 'A' : 'a');
    }
    return cyphered;
}

std::string decrypt(std::string const& cyphered, std::string const& key){
    std::string plain;
    for(std::size_t i = 0, j = 0, cypheredLen = cyphered.length(), keyLen = key.length();
        i != cypheredLen; ++i, ++j){
        if(j == keyLen)
            j = 0;
        plain += ( (cyphered.at(i) - key.at(j) + 26 ) % 26) +
        (std::isupper(cyphered.at(i)) ? 'A' : 'a');
    }
    return plain;
}

int main(){

    std::string s1 = "HELLO";
    std::string key1 = "ATOM";
    auto cyphered1 = encryptUpper(s1, key1);
    std::cout << cyphered1 << '\n';
    auto plain = decryptUpper(cyphered1, key1);
    std::cout << plain << '\n';

    std::string s2 = "hello";
    std::string key2 = "atom";

    auto cyphered2 = encryptLower(s2, key2);
    std::cout << cyphered2 << '\n';
    auto plain2 = decryptLower(cyphered2, key2);
    std::cout << plain2 << '\n';

    cyphered2 = encrypt(s2, key2);
    std::cout << cyphered2 << '\n';
    plain2 = decryptLower(cyphered2, key2);
    std::cout << plain2 << '\n';

    std::cout << "=========\n";
    auto c1 = encrypt(s1, key1);
    auto p1 = decrypt(c1, key1);
    std::cout << c1 << '\n' << p1 << '\n';

    auto c2 = encrypt(s2, key2);
    auto p2 = decrypt(c2, key2);
    std::cout << c2 << '\n' << p2 << '\n';
}
  • Why encryptLower() encrypts lowercase text in a wrong way? Although we know that the rule is:

    Ei = (Pi + Ki) mod 26
    

And decryption:

  Di = (Ei - Ki + 26) mod 26
  • Why when encrypting small letters I need to subtract the value of a from both the key and plain text characters then sum and mod 26? and why with capital letter I don't need so?

  • Are my generic encrypt() and decrypt functions correct? Thank you!

  • I am sure the problem is here:

      char P = 'H'; // 72
      char K = 'A'; // 65
    
      char C = (P + K) % 26; // 7
      C += 'A'; // 7 + 'A' =  H
      std::cout << C << '\n'; // H
    
      char p = 'h'; // 104
      char k = 'a'; // 97
    
      char c = (p + k) % 26; // 19
      c += 'a'; // 19 + 'a' = t
      std::cout << c << '\n'; // t
    
  • Normally using a key a or A yields the cyphered character unchanged like in the uppercase one but why doesn't work with lowercase?

Itachi Uchiwa
  • 3,044
  • 12
  • 26
  • Do you have any unit tests to validate your code? – tadman Jan 27 '21 at 20:22
  • Any reason you're using the quirky `std::size_t i{}` syntax instead of `std::size_t i = 0`? – tadman Jan 27 '21 at 20:22
  • @tadman: But it is not bad to so since the new standard allows and recommend such initialization. – Itachi Uchiwa Jan 27 '21 at 20:23
  • 1
    Have you used a debugger? – Henk Jan 27 '21 at 20:23
  • While the intent is to offer uniform methods of initialization, I'm not sure writing code this way makes it more readable. Your call though, just an observation. – tadman Jan 27 '21 at 20:26
  • 1
    To me the more interesting question is "Why is there different encrypt and decrypt routines for upper and lower case letters?" – user4581301 Jan 27 '21 at 20:26
  • @user4581301: You are completely right. Normally they try to give help on my topic not on a type of a valid initialization. Could I flag 'em "Off-Topic"? lol – Itachi Uchiwa Jan 27 '21 at 20:28
  • 1
    But one note on the `{}` initialization: when a class can be constructed with an `initializer_list` things get... weird. `std::vector vec(5);` provides a `vector` with 5 elements all set to 0. `std::vector vec{5};` provides a `vector` with 1 element set to 5. Easy thing to trip over if you aren't aware or a little distracted by your son hitting you with a foam baseball bat while working from home. – user4581301 Jan 27 '21 at 20:31
  • 1
    Other bit of unexpected nastiness: `std::isupper(cyphered.at(i))` can get really, really weird. So weird a problem that I'm just going to [link to a discussion of the problem](https://en.cppreference.com/w/cpp/string/byte/isupper#Notes). This happens with all of the c-style character manipulation functions. It's also something you don't see al that often, but when it does rear its ugly head, it's a serious mind. – user4581301 Jan 27 '21 at 20:38
  • 1
    Recommendation: Break `( (plain.at(i) + key.at(j) ) % 26) + 'a'` up into multiple steps on multiple lines lines and step through it to make sure it's behaving exactly the way you expect – user4581301 Jan 27 '21 at 20:46
  • 2
    You approached solving the problem exactly the right way. I was going to drop another hint that you should also investigate why the uppercase version worked, but it looks like Henk took care of that for me. `plain.at(i) - base + key.at(j) - base` where `base` is either `'A'` of `'a'` depending on upper or lower case will get both functions working exactly the same. As an added bonus, by adding `base` as a parameter to the encryption and decryption functions you can use the same function for both cases. less code is less room for bugs to hide. – user4581301 Jan 27 '21 at 21:22

1 Answers1

2

Consider the Ascii Table: http://www.asciitable.com/. If you write the characters as (65+i) and (65+j) and add them, with your uppercase method, you are getting 65+(65+i)+(65+j) \equiv 65+i+j \mod 26. You are lucky that 65 + 65 is divisible by 26 in the uppercase case!

For lowercases we do not have 97+97 divisible by 26.

Some tips for debugging and posting code on SO: The output of your code was wrong in the first letter of the method, so something was going wrong there. If you just consider that minimal example, it is much easier to spot the mistake. So try to produce a minimal reproducible example.

Henk
  • 826
  • 3
  • 14
  • Thank you. It looks really logical. But in my `encrypt` method it works fine because I subtract 'a' from the character then sum or subtract. – Itachi Uchiwa Jan 27 '21 at 21:12
  • Yes because you are adding and subtracting the shift. – Henk Jan 27 '21 at 21:13
  • Thanks a lot! Now I got it. so if small letters are numbered from a value that is divisible by 26 then they do the same work as capital letters. eg: consider small letters begin from 104. 'a' = 104 'b' = 105... ('a' + 'a' = 208) 208 % 26 =0. so to encrypt the letter 'h' with the key 'a': char c = ('h' + 'a') % 26 // normally 'h' still the same after encryption because summed with 'a' c = (111 + 104) % 26 = 215 % 26 = 7 c = 7 + 'a' = 'h' c = 'h' // correct – Itachi Uchiwa Jan 28 '21 at 11:34
  • Yeah, but this would be not really understandable. It is better to subtract the base from both letters, then add the resulting numbers and add the base again. – Henk Jan 28 '21 at 11:45
  • Yes I know. This is just for understanding reason only, In a real we should always convert them into 0-1-2... based latters. – Itachi Uchiwa Jan 28 '21 at 11:49