#!/usr/bin/perl
# Author: Maik Ernst
# History:
# 2016-11-25 - initial script
#
# LICENSE
# Hitachi, Ltd. All rights reserved
# Hitachi Data Systems products and services can be ordered only under the terms and conditions
# of the applicable Hitachi Data Systems agreements.
#
# NAME
# <various>
#
# SYNOPSIS
# see usage function below
#

use strict;
use warnings;
use Getopt::Long qw(:config no_ignore_case bundling); # Get options /  qw(:config bundling no_ignore_case) makes getopt case sensitive 

my $VERSION = "0.9.1";

my %HoA = (
1	=> [ 48,49,50,82,69,113,48,66,109,115,98,105,79,80,70,114,84,104,111,118,65,68,45,49,86,121,89,54,85,108],
2	=> [ 48,50,98,90,52,100,116,117,76,67,71,112,110,97,81,53,99,109,121,113,106,50,82,74,104,118,49,56,48],
3	=> [ 48,51,77,95,84,119,77,45,52,74,113,108,75,107,57,102,90,110,122,89,54,97,65,99,98,55,111,66],
4	=> [ 48,52,87,48,114,72,83,87,78,106,80,115,105,77,103,69,56,66,110,113,73,88,122,76,86,121,97],
5	=> [ 48,53,69,95,89,48,103,50,76,111,80,51,55,99,118,120,54,87,108,53,112,115,110,79,45,119],
6	=> [ 48,54,89,68,72,98,107,102,49,73,118,78,110,79,56,45,48,77,84,119,54,74,55,116,95],
7	=> [ 48,55,50,112,121,116,51,119,108,101,87,49,88,55,56,86,76,95,66,72,120,97,115,77],
8	=> [ 48,56,113,119,98,117,75,120,48,110,52,89,104,53,72,79,54,103,105,73,122,68,87],
9	=> [ 48,57,113,75,66,111,71,118,69,95,72,108,121,45,73,79,52,104,80,83,70,109],
10	=> [ 49,48,56,104,48,49,103,50,112,86,122,114,108,89,109,78,105,70,53,98,117],
11	=> [ 49,49,66,83,89,102,48,65,50,119,87,107,109,52,110,99,115,106,72,114],
12	=> [ 49,50,54,107,98,75,109,81,53,115,84,122,121,117,80,79,100,57,68],
13	=> [ 49,51,73,115,77,100,72,104,53,97,84,113,82,67,66,88,119,68],
14	=> [ 49,52,87,99,114,113,82,48,119,77,101,109,74,84,83,45,89],
15	=> [ 49,53,53,70,87,104,78,97,90,66,48,89,122,115,82,88],
16	=> [ 49,54,71,118,67,50,115,101,75,113,97,88,110,49,119],
17	=> [ 49,55,55,122,107,120,109,88,117,80,104,45,105,98],
18	=> [ 49,56,111,100,118,95,122,85,45,102,66,115,83],
19	=> [ 49,57,67,98,51,99,49,74,70,73,89,85],
20	=> [ 50,48,48,76,67,77,75,119,99,81,101],
21	=> [ 50,49,108,97,80,68,76,115,106,101],
22	=> [ 50,50,67,105,98,84,90,115,55],
23	=> [ 50,51,109,98,52,89,85,121,],
24	=> [ 50,52,87,81,103,55,89],
25	=> [ 50,53,79,114,67,73],
26	=> [ 50,54,71,120,116],
27	=> [ 50,55,74,101],
28	=> [ 50,56,87],
29	=> [ 50,57],
99  => [ 86,87,81,52,55,105,111,67,82,122,88,98,120,119,56,73,108,99,112,100,77,84,
		118,102,49,68,89,116,121,109,71,113,78,85,114,69,107,104,74,79,101,48,83,
		57,53,117,90,65,103,66,115,50,45,95,80,106,97,51,76,75,70,54,72,110 ],
);

# Must be alphanumeric, Hyphens (-), underscores (_)
my $phrase = "rest_pa33word";

# Check Flags
my $flag_version;
my $string_encode;
my $string_decode;
my $user_phrase;

GetOptions (
	'V|VER' => \$flag_version,
	'e|encode=s' => \$string_encode,
	'd|decode=s' => \$string_decode,
	'p|phrase=s' => \$user_phrase,
           ) or die;

# Check flags and print usage
if ($flag_version)
{
	print "Version: $VERSION";
	exit;
}

if ($string_encode)
{
	my $encoded = ENCODE($string_encode,$phrase,\%HoA);
	print "ENCODED: $encoded";
	exit 0;
}

if ($string_decode)
{
	if ((defined $user_phrase) and ($user_phrase eq $phrase))
	{
		if (length $string_decode > 2)
		{
			my $decoded = DECODE($string_decode,$phrase,\%HoA);
			print "DECODED: $decoded";
			exit 0;
		}
		else
		{
			print "WARNING: Less than 3 characters. Cannot be decrypted.";
			exit -1;
		}
	}
	else
	{
		print "WARNING: Sorry user phrase wrong. Cannot be decrypted.";
	}
}



##### DECODE

sub ENCODE
{
	my $par_string = $_[0];
	my $par_phrase = $_[1];
	my $par_HoA_ref = $_[2];
	
	my $length_string = 0;
	my $length_phrase = length $par_phrase;
	$length_string = length $par_string;
	
	# Check length
	if (($length_string == 0) or ($length_string > 29))
	{
		print "WARNING: Stringt length ($length_string) not supported. Must be 1 - 29 characters\n";
		exit -1;
	}
	
	# Split
	my @string_array = split(//,$par_string);
	my @string_array_ord;
	
	# Check Character, get ord
	for my $character (@string_array)
	{
		my $ord_character;
		$ord_character = ord($character);
		
		# check chars if valid
		my $char_check_flag = "bad";
		foreach my $c ( 0 .. $#{ $par_HoA_ref->{99} } ) 
		{
			if ($ord_character eq $par_HoA_ref->{99}[$c])
				{
					$char_check_flag = "good";
					last;
				}
		}
		if ($char_check_flag eq "bad")
		{
			print "WARNING: Character \"$character\" is not supported\n"; 
			print "WARNING: Supported Characters are: " . '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-';
			exit -1;
		}

		push (@string_array_ord, $ord_character);
	}
	

	my @char_ord_mixed = @string_array_ord;
	
	# Last index > 1 , first 2 are length info
	if ($#{ $par_HoA_ref->{$length_string} } > 1)
	{
		for my $i ( 2 .. $#{ $par_HoA_ref->{$length_string} } ) 
		{	
			push (@char_ord_mixed, $par_HoA_ref->{$length_string}[$i]);
		}
	}
	
	# reverse
	my @char_ord_mixed_rev = reverse(@char_ord_mixed);
	
	# add length info
	push (@char_ord_mixed_rev,$par_HoA_ref->{$length_string}[0],$par_HoA_ref->{$length_string}[1]);
		
	# do encoding
	my @string_array_ord_enc;
	for my $char_ord_orig (@char_ord_mixed_rev)
	{
		my $move = $length_phrase + 3;
		my $move_final;
		# check characters
		foreach my $c2 ( 0 .. $#{ $par_HoA_ref->{99} } ) 
		{
			if ($char_ord_orig eq $par_HoA_ref->{99}[$c2])
			{
			
				$move_final = $c2 + $move;
				#  if bigger than index start from beginning
				if ($move_final > $#{ $par_HoA_ref->{99} })
				{
					$move_final = $move_final - $#{ $par_HoA_ref->{99} } - 1;
				}
			}
		}
		push (@string_array_ord_enc, $par_HoA_ref->{99}[$move_final]);
	}
	
	# ord to chr
	my $string_enc_final;
	for my $char_ord_enc_1 (@string_array_ord_enc)
	{
		$string_enc_final = $string_enc_final . chr ($char_ord_enc_1);
	}
	
	return $string_enc_final;
}	

sub DECODE
{
	my $par_string = $_[0];
	my $par_phrase = $_[1];
	my $par_HoA_ref = $_[2];

	my $length_string = 0;
	my $length_phrase = length $par_phrase;
	$length_string = length $par_string;

	my @string_enc_array = split(//,$par_string);

	# chr to ord 
	my @string_array_ord_enc;
	for my $char_chr_enc_1 (@string_enc_array)
	{
	
		my $ord_character;
		$ord_character = ord($char_chr_enc_1);
		
		# check chars if valid
		my $char_check_flag = "bad";
		foreach my $c ( 0 .. $#{ $par_HoA_ref->{99} } ) 
		{
			if ($ord_character eq $par_HoA_ref->{99}[$c])
				{
					$char_check_flag = "good";
					last;
				}
		}
		if ($char_check_flag eq "bad")
		{
			print "WARNING: Something went wrong";
			exit -1;
		}
		
		push (@string_array_ord_enc, ord ($char_chr_enc_1));
	}
	
	# do decoding
	my @char_ord_dec;
	for my $char_ord_enc_1 (@string_array_ord_enc)
	{
		my $move = $length_phrase + 3;
		my $move_final;
		# check characters
		foreach my $c3 ( 0 .. $#{ $par_HoA_ref->{99} } ) 
		{
			if ($char_ord_enc_1 eq $par_HoA_ref->{99}[$c3])
			{
				$move_final = $c3 - $move;
				#  if bigger scmaller index start from last index
				if ($move_final < 0)
				{
					$move_final = $move_final + $#{ $par_HoA_ref->{99} } + 1;
				}
			}
		}
		push (@char_ord_dec, $par_HoA_ref->{99}[$move_final]);
	}
	
	# Get length
	my $digit2 = pop(@char_ord_dec);
	my $digit1 = pop(@char_ord_dec);
	my $orginal_string_length = chr ($digit1) . chr ($digit2);
	$orginal_string_length =~ s/^0+//g ; #remove leading zeroes
	
	# Reverse
	my @char_ord_mixed_notrev = reverse(@char_ord_dec);
	
	foreach ( 2 .. $#{ $par_HoA_ref->{$orginal_string_length} } ) 
	{
		pop (@char_ord_mixed_notrev);
	}
	
	my $final_decoded;
	for my $character (@char_ord_mixed_notrev)
	{
		$final_decoded = $final_decoded .  chr($character);
	}
	
	return $final_decoded;
}