#!/usr/bin/perl -w
use strict;

###  BlockDerypt.pl

##   Author:  Avi Kak  (kak@purdue.edu)
##   Date:    January 24, 2006

##   Call syntax:
##
##      BlockDecrypt.pl  encrypted_file_name  out_file_name
##
##   The decrypted plaintext is placed in out_file_name.

##   This script decrypts the encrypted output produced by
##   BlockEncrypt.pl.  It assumes that the encyption key is in file
##   named
##
##        keyfile.txt
##
##   This key states the random permutation order used for encryption.

##   Since the output of BlockEncrypt.pl is alwyas a multiple of
##   BLOCKSIZE number of bits, in this script we do not have to worry
##   about just a fraction of a BLOCKSIZE.  This allows the call to
##   sysread() to be placed directly in the condition of the while loop
##   in line (H).

##   As shown in lines (I) and (J), how we create a Bit::Vector object
##   from a block of BLOCKSIZE bits is exactly the same as in
##   BlockEncrypt.pl script.  When we invoke $bitvec->to_Bin() on the
##   bit vector, we get a bitstring.  (As stated in the documentation
##   written for BlockEncrypt.pl, the BLOCKSIZE bits put away in $buff
##   by sysread() are more aptly thought of as a bytestring.  But we
##   need for encryption and decryption are bitstrings.)  

##   In lines (K) and (L), we send the bit array constructed from the
##   bitstring to unpermute() subroutine that undoes the permutation
##   carried out by the encryption algorithm.


use Bit::Vector;                                                     
use constant BLOCKSIZE => 64;                                        

die "Needs two command-line arguments for in file and out file" 
    unless @ARGV == 2;                                               

open FROM, shift @ARGV or die "unable to open filename: $!";         #(A)
open TO, ">" . shift @ARGV or die "unable to open filename: $!";     #(B)
open KEYFILE, "keyfile.txt";                                         #(C)
binmode( FROM );                                                     #(D)
binmode( TO );                                                       #(E)

chomp( my $permute_indices = <KEYFILE> );                            #(F)
my @permute_indices = split / /, $permute_indices;                   #(G)

while ( sysread( FROM, my $buff, BLOCKSIZE/8 ) ) {                   #(H)
    my $bitvec = Bit::Vector->new(BLOCKSIZE);                        #(I)
    $bitvec->Block_Store( $buff );                                   #(J)

    my @data_arr = split //, $bitvec->to_Bin();                      #(K)
    my @decrypted = unpermute( \@data_arr, \@permute_indices );      #(L)

    my $decrypted_bit_string = join "", @decrypted;                  #(M)
    my $out_vector = Bit::Vector->new_Bin(BLOCKSIZE, 
                                        $decrypted_bit_string);      #(N)
    my $out_buff = $out_vector->Block_Read();                        #(O)
    syswrite( TO, $out_buff, BLOCKSIZE/8 );                          #(P)
}
 
sub unpermute {
    my $ref1 = shift;          # ref to the array to be unpermuted   #(Q)
    my $ref2 = shift;          # ref to the permutation array        #(R)
    my @data_arr = @$ref1;                                           #(S)
    my @permute_arr = @$ref2;                                        #(T)
    my @unpermuted_output;                                           #(U)
    for my $i (0..$#permute_arr) {                                   #(V)
        $unpermuted_output[ $permute_arr[$i] ] = $data_arr[$i];      #(W)
    }
    return @unpermuted_output;                                       #(X)
}
