Symmetric encryption with Caesar Cypher
Photo by Samantha Lam on Unsplash

Symmetric encryption with Caesar Cypher

The Caesar Cypher is a simple encryption technique that is around 2000 years old. It is categorized as a symmetric encryption type since it requires a private key that is used for both encryption and decryption. How does it work? This cypher algorithm is fairly simple, given an alphabet, each letter corresponds to a number. The key also translates to a number. Then each character of the secret message is shifted n number of times where n is the key number, and the character is substituted with the alphabet letter in the shifted position.

For instance the letters might be inserted in an array, therefore each letter will have a corresponding index position.

Suppose the secret message has the letter A in it and the key is 2 then the letter A will be shifted by two and becoming C.

Here follows my implementation in typescript. I have decided to make the cypher case sensitive and also replace empty spaces.

constructor() {
    this.alphanumericString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ';
    this.operations = new Map();
    this.operations.set('decrypt', (position: number, keyCode: number) => {
        return position - keyCode;
    });
    this.operations.set('encrypt', (position: number, keyCode: number) => {
        return position + keyCode;
    });
}

The constructor initializes the alpha numeric string of characters supported by this implementation. In addition to that it defines the operations for encrypting shift forward and decrypting shift backwards.

/**
* Helper method to calculated modulo for negative numbers
* (required due to a javascript modulo bug)
* @returns 
*/
private modulo(x: number, y: number) {
    return ((x % y) + y) % y;
}

Javascript built in modulo is not really a modulo function but rather a reminder. This helper methods allows % to work with negative integers.

private keyCode(key: string): number {
    if (key.length > 1) {
        throw new Error('Caesar Cypher Key is too long');
    }

    if (!key) {
        throw new Error('Invalid Caesar Cypher key')
    }

    const keyCode: number = this.alphanumericString.indexOf(key);

    if (keyCode < 0) {
        throw new Error('Invalid Caesar Cypher key')
    }

    return keyCode;
}

This method is responsible for returning the key code number. The function takes in a character and looks up the char position in the alpha numeric string variable.

private caesarCypher(text: string, key: string, operation: string) {
    // calculate the key number
    const keyCode: number = this.keyCode(key);
    // cast the text string to an array of string with single char
    const textChars: Array<string> = text.split('');

    // loop the char array
    return textChars.map((char) => {
        // find the index position in the alphanumeric string
        const position: number = this.alphanumericString.indexOf(char);
        // throw an error if the char is not found
        if (position < 0) {
            throw new Error(`char: ${char} not supported`);
        }

        // calculate the cypher position
        const cypherPosition: number = this.modulo(this.operations.get(operation)!.call(this, position, keyCode), this.alphanumericString.length);

        return this.alphanumericString[cypherPosition];
    })
    .reduce((prev, next) => {
        return prev + next;
    });
}

The caesarCypher function holds the logic of the algorithm. Once the key code is calculated then the function operates the character shifting. Both encrypt and decrypt follows the same logic exception made for the shifting. Encrypt shifts forward while decrypt shifts backwards. The method uses the operations variable that is defined in the constructor to determine the shift direction.

encrypt(text: string, key: string): string {
    return this.caesarCypher(text, key, 'encrypt');
}

decrypt(text: string, key: string): string {
    return this.caesarCypher(text, key, 'decrypt');
}

The snippets above shows how encryption and decryption becomes fairly simple to invoke.

test('it should allow to encrypt a valid text with a valid key', () => {
    const encryptedText: string = caesarCypher.encrypt('A message of love', 'M');
    expect(encryptedText).toBe('MLyq44msqL0rLx07q');
});

test('it should allow to decrypt and decrypt a valid text with a valid key', () => {
    const decryptedText: string = caesarCypher.decrypt('MLyq44msqL0rLx07q', 'M');
    expect(decryptedText).toBe('A message of love');
});

The tests shows how this caesarCypher implementation can be used to encrypt and decrypt messages. The complete source code for this example can be found here.