#!/usr/bin/perl

# use:
 # encrypt a file:  perl NitNatPlus.pl -e plaintextfilename encryptedfilename  
 # decrypt a file:  NitNatPlus.pl -d encryptedfilename decryptedfilename  
 # to input each key, type six digits separated by spaces, ending with RTN

 # the second key must be a *permutation* of digits 0-5 (hint hint).

# are we en- or de-crypting? sets $decrpt global.

ckcmd();

# read the input file into a string
$input = getinput();

# open the output file
getoutput();

#ask user for key --> array
@key = getkey();

#ask userfor 2nd key -->array
#must be a permutation!

@key2 = getkey();  

#for now no error checking

@inorder = (0,1,2,3,4,5);
for($i = 0; $i<6; $i++)
{
$invkey2[$key2[$i]] = $inorder[$i];
} 

print @key2, " ", @invkey2;


# unpack input string to array 
@arr = unpack( "C*", $input);

# how many chars is it?
$textlen = @arr;

$mask = 0x20;

# prepare the output array
    for ($byte = 0; $byte < $textlen; $byte++)
    {

#  To preserve all "printable ascii", such as delete, null, backspace, 
# and other troublesome bytes, use the following line.
# The output on disk will be fine, will decrypt to the original exactly,
# but looks ugly to print
#      $crypt[$byte] = 0x00 | ( $arr[$byte] & 0x40) ;

# The line  below limits your input and output character set to chars >= 0x40,
# which includes upper and lower-case letters and a few brackets.
# it changes space to `, comma to n, etc.

$crypt[$byte] = 0x40 ;

}


#decrypt
if ($decrypt)
{

    printf ("\n decrypting...");
for ($i = 0; $i<6; $i++)
{
  $tmask = $mask >> $i;

    for ($byte = 0; $byte < $textlen; $byte++)
    {
	 $inindex = ($byte-$key[$invkey2[$i]]) % $textlen;
	$bit =  ($arr[$byte] & $tmask) ;
	if  ($invkey2[$i] > $i) { $outbit = ( $bit >> ( $invkey2[$i] -$i));}
      elsif ($invkey2[$i] < $i) { $outbit = ($bit << ($i - $invkey2[$i] ));}
	  else {$outbit = $bit;}
	  
	$crypt[$inindex] =  $outbit | $crypt[$inindex];
	
     }
}
}

else  #encrypt
{
    printf ("\n encrypting...");
for ($i = 0; $i<6; $i++)
{
  $tmask = $mask >> $i;

    for ($byte = 0; $byte < $textlen; $byte++)
    {
      $inindex = ($byte+$key[$i]) % $textlen;

	$bit =  ($arr[$byte] & $tmask) ;
	if  ($key2[$i] > $i) { $outbit = ( $bit >> ( $key2[$i] -$i));}
	  elsif ($key2[$i] < $i) { $outbit = ($bit << ($i - $key2[$i] ));}
	  else {$outbit = $bit;}

	$crypt[$inindex] =  $outbit | $crypt[$inindex];

	
    }
}


}

# not quite so simple but not much better I think...


# array to string
$encrypt = pack ("C*", @crypt);


# write it out
printf (OUTFID "%s\n", $encrypt);

#clean up
close(OUTFID);


################## that's all, folks! ################

#### some subrs...


# read the key into an array
sub getkey
{
    my @keyway;
    printf ("\n Enter space-delimited 6-digit key: ");
    $a = <STDIN>;
#    printf ("\n input: %s ", $a);
    chop($a);
#    printf ("\n input: %s ", $a);
    @keyway = split(/ /, $a);
    return @keyway;
}



# read input into a string, toss out carriage returns.
sub getinput
{
    my $fname ;
    $fname = shift(@ARGV);
    $out = "";
    unless (open INFID, $fname)
    {die "\n Can't open $fname for input!\n";}

while ($line = <INFID>)
{
    chop($line);
    $out = $out  . $line . " " ;
}
chop($out);  
return $out;


}


# read the fake option flag
sub ckcmd()
{

$action = shift(@ARGV);  
#print "\n", $action;
if ($action eq "-d")
{ $decrypt = 99; return;}
if ($action eq "-e")
{ $decrypt = 0; return;}
printf ("\n usage: nitnat <-d | -e> infname outfname  to De or encrypt.");
exit;
}

#return a file ID for output
sub getoutput()

{
    my $fname;
    $fname = shift(@ARGV);
  unless ( open (OUTFID, ">$fname"))
  { die "\nCan't open $fname for output!\n";}
}

