Search code examples
cencryptionvigenere

Vigenere's cipher in C


I have written a program in C that takes plain-text and a password and produces a cipher-text using vigenere's cipher. While the code produces the correct output most of the time, I have found an example where it produces an unexpected output and I cannot find the issue myself. The output is as such:

jess@laptop:~/Desktop/programming/C/current/vigenere$ ./vigenere lemon attackatdawn
LXF OPV EFR NHR [0002]

That box at the end doesn't appear as it should, it is meant to represent when bash tries to display ascii character 2, but copy and paste doesn't show it correctly. This is the example text from Wikipedia for the cipher, and it's the only text I've found that breaks my program (I don't know what the cause is, so I cannot replicate it) but I'm sure there are more strings that will produce similar results. I suspect that I have done something that produces undefined behaviour but I'm not sure. What have I done wrong here? My code:

// vigenere.c - Takes a plaintext and a cipher key from argv[1] and argv[2] and produces the cipher text according to Vigenere's cipher

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

void string_clean(char *source) //Courtesy (mostly) of user 'Aaron' of StackOverflow
{
    char *i = source;
    char *j = source;
    
    while(*j != 0) {
        *i = *j++;
        if( *i != ' ' && (isupper(*i) || islower(*i)) )
            i++;
    }
    
    *i = 0;
}

char *vigenere_enc(char plain[], char cipher[])
{
    char *cipher_text;
    
    string_clean(plain);
    string_clean(cipher);
    
    int plain_len = strlen(plain);
    int cipher_len = strlen(cipher);
    
    if( !(cipher_text = calloc(plain_len, sizeof(char))) )
        return 0;
    
    for(int i = 0; i < cipher_len; i++) {
        if(isupper(cipher[i]))
            cipher[i] -= 'A';
        else if(islower(cipher[i]))
            cipher[i] -= 'a';
    }
    
    int j = 0;
    
    for(int i = 0; i < plain_len; i++, j++) {
        if(j == cipher_len)
            j = 0;
        
        if(isupper(plain[i]))
            plain[i] -= 'A';
        else if(islower(plain[i]))
            plain[i] -= 'a';
        
        cipher_text[i] = ((plain[i] + cipher[j]) % 26) + 'A';
    }
    return cipher_text;
}

int main(int argc, char *argv[])
{
    if(argc != 3)
        return 1;
    char *cipher = vigenere_enc(argv[2], argv[1]);

    for(int i = 0; i < strlen(cipher); i++) {
        if(i % 3 == 0 && i != 0)
            putchar(' ');
        putchar(cipher[i]);
    }
    
    putchar('\n');
    
    return 0;
}

All and any help/suggestions are greatly appreciated!


Solution

  • You need to NUL-terminate your output string. :-( That also means your call to calloc (you really should just use malloc, though) should specify plain_len + 1, not just plain_len.