Auto Key Cipher

Cryptography has always been an interesting subject for me. Never really did much coding that involved it however. A little while back someone had asked for help writing a program that performs an Auto Key Cipher, so I figured I'd try my hand at making a proper cipher. I wrote two versions. One DLL, class based written in C# (with GUI). The other procedural based in Perl.

C# DLL with GUI (source)
Perl

Here's the Perl code if you don't want to download it.

#!/usr/bin/perl -w

## This program is free software. It comes without any warranty, to
## the extent permitted by applicable law. You can redistribute it
## and/or modify it under the terms of the Do What The Fuck You Want
## To Public License, Version 2, as published by Sam Hocevar. See
## http://sam.zoy.org/wtfpl/COPYING for more details.

use strict;
use warnings;
use File::Basename;

my $inputString;
my $keyString;
my $type = 'e';		# Default is to encode.
my @flags;			# For storing caps (1) and spaces (2).

if($#ARGV == 2) {
	my $typeArg = shift @ARGV;
	$inputString = shift @ARGV;
	$keyString = shift @ARGV;
	my $tString;
	
	## Are we encoding or decoding?
	if($typeArg eq '-e' || $typeArg eq '--encode') {
		$tString = 'Encode';
		$type = 'e';
		
	} elsif( $typeArg eq '-d' || $typeArg eq '--decode' ) {
		$tString = 'Decode';
		$type = 'd';
		
	## Default to encode.
	} else {
		print "[ERROR] Unknown switch '$typeArg'; Using '-e'.\n";
		$tString = 'Encode';
	}
	print "==$tString==\nInput string: $inputString\nKey string: $keyString\n";

## Encode/Decode excluded in command line.
} elsif( $#ARGV == 1 ) {
	$inputString = shift @ARGV;
	$keyString = shift @ARGV;
	print "[Encode] Input string: $inputString\nKey string: $keyString\n";

## Wrong number of arguments. Can only have 0, 2, or 3.
} elsif( $#ARGV == 0 || $#ARGV > 2) {
	print "Usage: ".basename($0)." [-e|--encode|-d|--decode] [<input string> <key string>]\n";
	exit;
}

## Input and Key strings not given in command line; Ask for them.
if( !$inputString || !$keyString ) {
	print "Input string: ";
	$inputString = <STDIN>;
	chomp $inputString;

	print "Key string: ";
	$keyString = <STDIN>;	# 'n' is rot13
	chomp $keyString;
}

# Array of the alphabet.  Everything revolves around this being correct.
my @letters = qw(a b c d e f g h i j k l m n o p q r s t u v w x y z);

# Make the key lowercase
$keyString = lc($keyString);

# Initilaze arrays to store offsets
my @inputOffsets;
my @keyOffsets;

my $x; # Current index

# Find the offsets for each letter in the key string
print "Key offsets: ";
foreach my $keyLetter (split(//, $keyString)) {
	
	## Found something other than a letter.
	if( $keyLetter !~ /[a-z]/i ) {
		print "\n[FATAL ERROR] Key can only contain letters!";
		exit 2;
	}

	## Find the current letter in the alphabet array
	for($x = 0; $x <= $#letters; $x++) {
		if( $keyLetter eq $letters[$x] ) {
			# Store the offset we found
			push(@keyOffsets, $x);
			print "$x ";
		}
	}
}

# Find the offsets for each letter in the input string
print "\nInput offsets: ";
foreach my $inputLetter (split(//, $inputString)) {
	
	## Found something other than a letter or a space.
	if( $inputLetter !~ /[a-z ]/i ) {
		print "\n[FATAL ERROR] Input can only contain letters and spaces!";
		exit 3;
	
	## Found a space.
	} elsif( $inputLetter eq ' ' ) {
		push(@flags, 2);
		push(@inputOffsets, -1);
		next;
	}

	# Find the current letter in the alphabet array
	for($x = 0; $x <= $#letters; $x++) {
		if( lc($inputLetter) eq $letters[$x] ) {
			# Store the offset we found
			push(@inputOffsets, $x);
			
			## Found a capital letter.
			if($inputLetter ne lc($inputLetter)) { push(@flags, 1); }
			## Lowercase letter.
			else { push(@flags, 0); }
			
			print "$x ";
		}
	}
}

# Make the key string as long as the input string
my $z;	# Current letter in the input string
my $y = 0;	# Current letter in the key string

# Iterate once for each letter in the input string past the length of the key string
for( $z = $#keyOffsets; $z <= $#inputOffsets; $z++ ) {
	# Add new letter to the end
	push(@keyOffsets, $keyOffsets[$y]);
	
	# Increment index
	$y++;
	
	# Reset index when it goes out of bounds
	if($y > $#keyOffsets) {
		$y = 0;
	}
}

my $result;		# Encoded/Decoded string.

print "\nFlags: ". join(' ', @flags);
print "\nEffective offsets: ";

# Output loop
my $i;
for( $i = 0; $i < length($inputString); $i++) {
	## Found a space so skip the math.
	if($flags[$i] == 2) {
		$result .= ' ';
		next;
	}
	
	my $newIdx;		# Encoded/Decoded letter's offset.
	if( $type eq 'e' ) {
		## Add the two offsets to encode.
		$newIdx = $inputOffsets[$i] + $keyOffsets[$i];
		
	} else {
		## Subtract the two offsets to decode.
		$newIdx = $inputOffsets[$i] - $keyOffsets[$i];
	}
	
	## Loop around to 'a' if we went past 'z' (encoding)
	if( $newIdx > 25 ) {
		$newIdx -= 26;
	
	## Loop around to 'z' if we went past 'a' (decoding)
	} elsif( $newIdx < 0 ) {
		$newIdx += 26;
	}
	
	print "$newIdx ";
	
	# Print the new letter
	if($flags[$i] == 1) {
		## Preserve capitals.
		$result .= uc($letters[$newIdx]);
	} else {
		$result .= $letters[$newIdx];
	}
}

## Print the results
print "\n\"$inputString\" + \"$keyString\" = \"$result\"";