#!/usr/bin/perl
#
#  RC4 Simple FILE Encryption/Decryption
#
#  Copyright 2018 (c) Todor Donev 
#  <todor.donev at gmail.com>
#  https://ethical-hacker.org/
#  https://facebook.com/ethicalhackerorg
#
#  Description:
#  RC4 generates a pseudorandom stream of bits 
#  (a keystream). As with any stream cipher, 
#  these can be used for encryption by combining 
#  it with the plaintext using bit-wise exclusive-or; 
#  decryption is performed the same way (since 
#  exclusive-or with given data is an involution). 
#  (This is similar to the Vernam cipher except 
#  that generated pseudorandom bits, rather than a 
#  prepared stream, are used.) To generate the 
#  keystream, the cipher makes use of a secret 
#  internal state which consists of two parts:
#
#  o  A permutation of all 256 possible bytes 
#     (denoted "S" below).
#   
#  o Two 8-bit index-pointers (denoted "i" and "j").
#
#  The permutation is initialized with a variable 
#  length key, typically between 40 and 2048 bits,
#  using the key-scheduling algorithm (KSA). Once 
#  this has been completed, the stream of bits is 
#  generated using the pseudo-random generation 
#  algorithm (PRGA).
#
#  [todor@paladium ~]$ perl rc4file.pl ethack test.txt test.rc4
#  [ RC4 Simple FILE Encryption/Decryption
#  [ =======
#  [ Author: Todor Donev <todor.donev at gmail.com>
#  [ https://ethical-hacker.org/
#  [ https://fb.com/ethicalhackerorg
#  [ =======
#  [ Opening test.txt
#  [ Calculation..
#  [ Ready and test.rc4 file is closed.
#  [todor@paladium ~]$ perl rc4file.pl ethack test.rc4 test.rc4.encoded.txt && cat test.rc4.encoded.txt
#  [ RC4 Simple FILE Encryption/Decryption
#  [ =======
#  [ Author: Todor Donev <todor.donev at gmail.com>
#  [ https://ethical-hacker.org/
#  [ https://fb.com/ethicalhackerorg
#  [ =======
#  [ Opening test.rc4
#  [ Calculation..
#  [ Ready and test.rc4.encoded.txt file is closed.
#  Ethical Hacker Bulgaria 2018
#  International Cybersecurity Association
#  [todor@paladium ~]$
#
#  Disclaimer:
#  This or previous programs is for Educational
#  purpose ONLY. Do not use it without permission.
#  The usual disclaimer applies, especially the
#  fact that Todor Donev is not liable for any
#  damages caused by direct or indirect use of the
#  information or functionality provided by these
#  programs. The author or any Internet provider
#  bears NO responsibility for content or misuse
#  of these programs or any derivatives thereof.
#  By using these programs you accept the fact
#  that any damage (dataloss, system crash,
#  system compromise, etc.) caused by the use
#  of these programs is not Todor Donev's
#  responsibility.
#
#  Use them at your own risk!

use warnings;
use strict;
use open ':std', ':encoding(UTF-8)';

print "[ RC4 Simple FILE Encryption/Decryption\n";
print "[ =======\n";
print "[ Author: Todor Donev <todor.donev at gmail.com>\n";
print "[ https://ethical-hacker.org/\n";
print "[ https://fb.com/ethicalhackerorg\n";
print "[ =======\n";
die   "[ Usage: $0 <passphrase> <input file> <output file>\n" if(@ARGV != 3);
our $MAX_CHUNK_SIZE = 1024 unless $MAX_CHUNK_SIZE;
my $passphrase = $ARGV[0];
print "[ Opening $ARGV[1]\n";
open(IN, $ARGV[1]) or die "[ Error: Can't read $ARGV[1]: $!\n";
die "[ Error: The file is empty" if (-z $ARGV[1]);
open(OUT, ">$ARGV[2]") or die "[ Error: Can't wrote $ARGV[2]: $!\n";
binmode(IN); 
binmode(OUT);
print "[ Calculation..\n";
my $bytes = (stat $ARGV[1])[7];
while(sysread(IN,my $in,$bytes)){
     print OUT rc4($passphrase, $in);
}
print "[ Ready and $ARGV[2] file is closed.\n";
close(IN);
close(OUT);

sub rc4 {
    my $self;
    my(@state, $x, $y);
    if (ref $_[0]){
        $self = shift;
    @state = @{$self->{state}};
    $x = $self->{x};
    $y = $self->{y};
    } else {
        @state = init(shift);
    $x = $y = 0;
    }
    my $message = shift;
    my $num_pieces = do{
    my $num = length($message) / $MAX_CHUNK_SIZE;
    my $int = int $num;
    $int == $num ? $int : $int+1;
    };
    for my $piece (0..$num_pieces-1){
    my @message = unpack "C*", substr($message, $piece * $MAX_CHUNK_SIZE, $MAX_CHUNK_SIZE);
    for ( @message ) {
        $x = 0 if ++$x > 255;
        $y -= 256 if ($y += $state[$x]) > 255;
        @state[$x, $y] = @state[$y, $x];
        $_ ^= $state[($state[$x]+$state[$y]) % 256];
    }
    substr($message, $piece * $MAX_CHUNK_SIZE, $MAX_CHUNK_SIZE) = pack "C*", @message;
    }
    if ($self) {
    $self->{state} = \@state;
    $self->{x} = $x;
    $self->{y} = $y;
    }
    $message;
}

sub init {
    my @k = unpack('C*',shift);
    my @state = 0..255;
    my $y = 0;
    for my $x (0..255){
    $y = ($k[$x % @k]+$state[$x]+$y) % 256;
    @state[$x, $y] = @state[$y, $x];
    }
    wantarray ? @state : \@state;
}