Cryptography: Ciphered Text in C

CS50 Project 2 Implementation
Captain X Cipher
July 2025
CS50, C Programming, Cryptography

Cryptography involves converting plaintext to ciphertext using a key. In this CS50 project, you'll learn how to write a C program that converts plain text to ciphered text using a 26-letter alphabetical key.

Project Requirements

  • Validate a 26-character key from command line arguments
  • Ensure key contains only letters with no repeats
  • Convert plaintext to ciphertext using substitution cipher
  • Preserve case and non-alphabetic characters

Step 1: Command Line Argument for Key

We start by validating the command line argument. The key must be a 26-character string containing only letters with no repeats.

substitution.c
int main(int argc, string argv[])
{
    // Validate command line arguments
    if (argc != 2 || strlen(argv[1]) != 26)
    {
        printf("Usage: ./substitution key or Key must contain 26 characters\n");
        return 1;
    }
    
    int len = strlen(argv[1]);
    
    // Validate key characters
    for (int i = 0; i < len; i++)
    {
        if (!isalpha(argv[1][i]))
        {
            printf("Key must contain only letters\n");
            return 1;
        }
        
        // Check for duplicate letters (case-insensitive)
        for (int j = i + 1; j < len; j++)
        {
            if (toupper(argv[1][i]) == toupper(argv[1][j]))
            {
                printf("Letters cannot be repeated\n");
                return 1;
            }
        }
    }
}

Step 2: Enciphering Plain Text

This function takes the key and plaintext, converting each alphabetic character to its corresponding cipher character while preserving case and non-alphabetic characters.

Substitution Process

  • For uppercase letters: index = letter - 'A'
  • For lowercase letters: index = letter - 'a'
  • Replace with corresponding key character
  • Preserve original case in output
  • Non-alphabetic characters remain unchanged
substitution.c
string enciphertext(string text, string key)
{
    int len = strlen(text);
    string ciphertext = malloc(len + 1);
    int index;
    
    for (int i = 0; i < len; i++)
    {
        if (isupper(text[i]))
        {
            // Convert uppercase letter
            index = text[i] - 'A';
            ciphertext[i] = toupper(key[index]);
        }
        else if (islower(text[i]))
        {
            // Convert lowercase letter
            index = text[i] - 'a';
            ciphertext[i] = tolower(key[index]);
        }
        else
        {
            // Preserve non-alphabetic characters
            ciphertext[i] = text[i];
        }
    }
    
    // Null-terminate the string
    ciphertext[len] = '\0';
    return ciphertext;
}

Step 3: Full Program

The complete program integrates key validation and text enciphering to create a substitution cipher tool.

substitution.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <cs50.h>

// Function prototype
string enciphertext(string text, string key);

int main(int argc, string argv[])
{
    // Validate command line arguments
    if (argc != 2 || strlen(argv[1]) != 26)
    {
        printf("Usage: ./substitution key\nKey must contain 26 characters\n");
        return 1;
    }
    
    int len = strlen(argv[1]);
    
    // Validate key
    for (int i = 0; i < len; i++)
    {
        if (!isalpha(argv[1][i]))
        {
            printf("Key must contain only letters\n");
            return 1;
        }
        
        for (int j = i + 1; j < len; j++)
        {
            if (toupper(argv[1][i]) == toupper(argv[1][j]))
            {
                printf("Letters cannot be repeated\n");
                return 1;
            }
        }
    }
    
    string key = argv[1];
    string text = get_string("plaintext: ");
    string ciphertext = enciphertext(text, key);
    printf("ciphertext: %s\n", ciphertext);
    free(ciphertext);
    return 0;
}

string enciphertext(string text, string key)
{
    int len = strlen(text);
    string ciphertext = malloc(len + 1);
    int index;
    
    for (int i = 0; i < len; i++)
    {
        if (isupper(text[i]))
        {
            index = text[i] - 'A';
            ciphertext[i] = toupper(key[index]);
        }
        else if (islower(text[i]))
        {
            index = text[i] - 'a';
            ciphertext[i] = tolower(key[index]);
        }
        else
        {
            ciphertext[i] = text[i];
        }
    }
    
    ciphertext[len] = '\0';
    return ciphertext;
}

Key Considerations

  • Memory Management: Properly allocate and free memory for ciphertext
  • Case Preservation: Maintain original case of input characters
  • Input Validation: Ensure key meets all requirements before processing
  • Error Handling: Provide clear error messages for invalid input
  • Efficiency: Optimized character processing with O(n) complexity

Example Usage

Input: ./substitution YTNSHKVEFXRBAUQZCLWDMIPGJO

Plaintext: hello, world!

Ciphertext: jrssb, ybwsp!