
=head1 NAME

CSCI162_RC4 - Perl implementation of ARCFOUR

=head1 DESCRIPTION

This is an implementation of the ARCFOUR stream cipher (unofficial RC4).

=head1 EXPORTS

=over 4

=cut

package CSCI162_RC4;

use warnings;
use strict;
use Carp;

use base 'Exporter';
our @EXPORT = qw/rc4_encrypt rc4_decrypt rc4_encrypt_base64 rc4_decrypt_base64 rc4_translate/;
our @EXPORT_OK = @EXPORT;

my $BOX_LEN = 256;


# raw_to_array $rawstring
# Returns the array of byte values in $rawstring.
sub raw_to_array ($) {
	return (unpack('C*',$_[0]));
}

# array_to_raw $byteval1, $byteval2, ...
# Returns a string composed of the byte values of the passed array.
sub array_to_raw (@) {
	return pack('C*',@_);
}

# raw_to_hex $rawstring
# Returns the hex string associated with a string of raw bytes.
sub raw_to_hex ($) {
	return unpack('H*',$_[0]);
}

# hex_to_raw $hexstring
# Returns the raw string associated with an array of hex values.
sub hex_to_raw ($) {
	my $hex = shift;

	# There must be no leading sign or 0x.
	$hex =~ s/^-?(?:0[Xx])?//o;
	# There must be no non-hex digits.
	$hex =~ s/[^0-9A-Za-z]+//o;

	# The number of hex digits must be even.
	$hex = "0$hex" if length($hex) % 2;

	# Pack it.
	return pack('H*',$hex);
}

# key_to_box $key
# Returns the box (an array) associated with a key.
sub key_to_box ($) {
	# We want the key that's been input as a byte array.
	my @key = raw_to_array $_[0];

	# First loop sets each element to its index value.
	# We don't need to do that in Perl, because that's
	# just a range!
	my @box = (0 .. $BOX_LEN-1);

	my $i2 = 0;
	my $len = scalar @key; # length of key array

	# The second loop depends on the key string.
	for(my $i=0; $i<@box; ++$i) {
		$i2 = ($i2 + $box[$i] + $key[$i%$len]) % $BOX_LEN;
		# Swap the bytes.
		($box[$i],$box[$i2]) = ($box[$i2],$box[$i]);
	}

	# That's it.
	return @box;
}


=item rc4_encrypt $key, $message

Returns a hexadecimal string of the encrypted form of $message.

=cut

sub rc4_encrypt ($$) {
	return raw_to_hex( rc4_translate($_[0],$_[1]) );
}

=item rc4_decrypt $key, $cipher

Returns the decryption of $cipher (which must be passed as hexadecimal).

=cut

sub rc4_decrypt ($$) {
	return rc4_translate($_[0], hex_to_raw($_[1]));
}

# As a bonus, this module will also allow Base64 instead of hex for the
# encrypted form.  Base64 is an encoding that encodes information from
# 3 bytes into 4 7-bit-safe ASCII characters (suitable for sending over
# most protocols), versus the 6 that hex would take.

use MIME::Base64;

=item rc4_encrypt_base64 $key, $plain

Returns a Base64 string of the encrypted form of $message.

=cut

sub rc4_encrypt_base64 ($$) {
	return encode_base64(rc4_translate($_[0],$_[1]));
}

=item rc4_decrypt_base64 $key, $cipher

Returns the decryption of $cipher (which must be passed as Base64).

=cut

sub rc4_decrypt_base64 ($$) {
	return rc4_translate($_[0], decode_base64($_[1]));
}

=item rc4_translate $key, $rawstring

This function actually does all the work of encryption and decryption, which are
for all practical purposes identical.  The only difference is the input and
output format; the encrypt wrappers transcode the output of this function, while
the decrypt wrappers transcode the message parameter to this function.

This function takes raw data and outputs raw data; it can be used to implement other
encodings.

=cut

sub rc4_translate ($$) {
	my $i = 0;
	my $j = 0;

	# Get the box for this key.
	my @box = key_to_box $_[0];

	# Get the bytes for the message.
	my @msg = raw_to_array $_[1];

	# An array for the output. 
	my @out = ();

	# Encrypt/decrypt
	# (This is the PRGA - pseudo-random generation algorithm)
	for(my $idx=0; $idx < @msg; ++$idx) {
		# Set the new index values
		$i = ($i + 1) % $BOX_LEN;
		$j = ($j + $box[$i]) % $BOX_LEN;
		# Swap the indices
		($box[$i],$box[$j]) = ($box[$j],$box[$i]);
		# Now, do some xoring
		push(@out, $msg[$idx] ^ $box[ ($box[$i]+$box[$j]) % $BOX_LEN ]);
	}

	# Stringify
	return array_to_raw @out;
}



1;

__END__

=back

=head1 AUTHORS

Written for CSCI 162 Cryptography Spring 2005 at The George Washington University by Anasse Bari and Peter S. May
under Dr. Poorvi Vora.

=head1 NOTICE

This program alleges no compatibility or relationship with the official RC4 stream cipher
or with RSA Data Security.

=cut
