Search code examples
c++encryptionvigenere

Why my Vigenere encryption function for small letters doesn't work correctly?


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?


Solution

  • 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.