Perl — 6

Himashi Karunathilake
11 min readAug 22, 2023

--

Image from https://www.vectorlogo.zone/logos/perl/perl-ar21.svg
Link to Part 1: https://himashikarunathilake.medium.com/perl-1-5a5f4ec8c251
Link to Part 2: https://himashikarunathilake.medium.com/perl-2-12f31be96028
Link to Part 3: https://himashikarunathilake.medium.com/perl-3-daf8722151fa
Link to Part 4: https://himashikarunathilake.medium.com/perl-4-2215fa18efc3
Link to Part 5: https://himashikarunathilake.medium.com/perl-5-81db037b76a0

In this article let’s look at using our Perl skills to work on two simple projects.

NOTE:

Please note that the projects mentioned in this article are strictly
FOR EDUCATIONAL PURPOSES ONLY.

A Secure Text Messaging App

Link to Project: https://github.com/Himashi-Karunathilake/Perl/tree/master/Projects/secure_text_messager

The primary objective of this project is to demonstrate the concept of secure messaging through encryption. In this section, we will therefore, try to build a basic chat application that allows two users to exchange messages securely by encrypting the messages using the Advanced Encryption Standard (AES) algorithm.

In this program, we will first need to obtain a secret key from the user to be used for encryption. To obtain this input, we will be using the module Term::ReadKey so that the input will not be visible on the terminal.

#!/usr/bin/perl

# Objective of the Project: The following tool can be used for encrypted communication between two users.

use strict;
use warnings;
use feature "say";
use Term::ReadKey;

say "=============== WELCOME TO SECURE CHAT MESSAGING ===============";
say "";

# Obtain user names
print "What is your name? ";
my $user_a = <STDIN>;
chomp($user_a);
print "Who would you like to chat with? ";
my $user_b = <STDIN>;
chomp($user_b);

say "";

# Obtain the encryption key (16, 24, or 32 bytes for 128, 192, or 256 bits)
print "Enter the encryption / secret key you would like to use: ";

# Disable terminal echo while reading the encryption key
ReadMode('noecho');
my $encryption_key = ReadLine(0);
chomp($encryption_key);

# Restore terminal echo
ReadMode('restore');

Once we have the secret key, we will now initialize the Cipher Block Chaining (CBC) cipher with the AES Rijndael algorithm using the Crypt::CBC module.

#!/usr/bin/perl

# Objective of the Project: The following tool can be used for encrypted communication between two users.

use strict;
use warnings;
use feature "say";
use Crypt::CBC;
use Term::ReadKey;

say "=============== WELCOME TO SECURE CHAT MESSAGING ===============";
say "";

# Obtain user names
print "What is your name? ";
my $user_a = <STDIN>;
chomp($user_a);
print "Who would you like to chat with? ";
my $user_b = <STDIN>;
chomp($user_b);

say "";

# Obtain the encryption key (16, 24, or 32 bytes for 128, 192, or 256 bits)
print "Enter the encryption / secret key you would like to use: ";

# Disable terminal echo while reading the encryption key
ReadMode('noecho');
my $encryption_key = ReadLine(0);
chomp($encryption_key);

# Restore terminal echo
ReadMode('restore');

# Initialize the CBC cipher with Rijndael (AES) algorithm
my $cipher = Crypt::CBC->new(
-key => $encryption_key,
-cipher => 'Rijndael',
-header => 'none',
-iv => '1234567890123456' # IV should be exactly 16 bytes long
);

We will now initialize a conversation flag called $continue and set its value as 1. For as long as this value is 1, the conversation will go back and forth between the two users.

#!/usr/bin/perl

# Objective of the Project: The following tool can be used for encrypted communication between two users.

use strict;
use warnings;
use feature "say";
use Crypt::CBC;
use Term::ReadKey;

say "=============== WELCOME TO SECURE CHAT MESSAGING ===============";
say "";

# Obtain user names
print "What is your name? ";
my $user_a = <STDIN>;
chomp($user_a);
print "Who would you like to chat with? ";
my $user_b = <STDIN>;
chomp($user_b);

say "";

# Obtain the encryption key (16, 24, or 32 bytes for 128, 192, or 256 bits)
print "Enter the encryption / secret key you would like to use: ";

# Disable terminal echo while reading the encryption key
ReadMode('noecho');
my $encryption_key = ReadLine(0);
chomp($encryption_key);

# Restore terminal echo
ReadMode('restore');

# Initialize the CBC cipher with Rijndael (AES) algorithm
my $cipher = Crypt::CBC->new(
-key => $encryption_key,
-cipher => 'Rijndael',
-header => 'none',
-iv => '1234567890123456' # IV should be exactly 16 bytes long
);

say "";

# Initialize the conversation flag
my $continue = 1;

Now, we will declare three subroutines that will be used throughout the program called obtain_message, encrypt_message, and decrypt_message.

#!/usr/bin/perl

# Objective of the Project: The following tool can be used for encrypted communication between two users.

use strict;
use warnings;
use feature "say";
use Crypt::CBC;
use Term::ReadKey;

sub obtain_message {
print "Enter your message: ";
my $message = <STDIN>;
chomp($message);
return $message;
}

sub encrypt_message {
my $message = shift;
my $cipher = shift;
my $encrypted_message = $cipher->encrypt($message);
return $encrypted_message;
}

sub decrypt_message {
my $encrypted_message = shift;
my $cipher = shift;
my $decrypted_message = $cipher->decrypt($encrypted_message);
return $decrypted_message;
}

say "=============== WELCOME TO SECURE CHAT MESSAGING ===============";
say "";

# Obtain user names
print "What is your name? ";
my $user_a = <STDIN>;
chomp($user_a);
print "Who would you like to chat with? ";
my $user_b = <STDIN>;
chomp($user_b);

say "";

# Obtain the encryption key (16, 24, or 32 bytes for 128, 192, or 256 bits)
print "Enter the encryption / secret key you would like to use: ";

# Disable terminal echo while reading the encryption key
ReadMode('noecho');
my $encryption_key = ReadLine(0);
chomp($encryption_key);

# Restore terminal echo
ReadMode('restore');

# Initialize the CBC cipher with Rijndael (AES) algorithm
my $cipher = Crypt::CBC->new(
-key => $encryption_key,
-cipher => 'Rijndael',
-header => 'none',
-iv => '1234567890123456' # IV should be exactly 16 bytes long
);

say "";

# Initialize the conversation flag
my $continue = 1;

while ($continue) {
print "\n~~~~~~~~~~ $user_a, you are connected to $user_b. ~~~~~~~~~~\n\nWould you like to message $user_b (y/n)? ";
my $response_a = <STDIN>;
chomp($response_a);

if ($response_a eq "y") {
# Obtain the message from User A
my $message_a = obtain_message();

# Encrypt the message
my $encrypted_message_a = encrypt_message($message_a, $cipher);

say "\nYour message has been encrypted: $encrypted_message_a";

# User B receives the message
say "\n\n\n$user_b, you have received a message from $user_a.\nDecrypting message..........";
my $decrypted_message_b = decrypt_message($encrypted_message_a, $cipher);
say "\n\nDecrypted message: $decrypted_message_b";

say "";

print "$user_b, would you like to message $user_a (y/n)? ";
my $response_b = <STDIN>;
chomp($response_b);

if ($response_b eq "y") {
# User B replies
my $reply_b = obtain_message();

# Encrypt the reply
my $encrypted_reply_b = encrypt_message($reply_b, $cipher);

say "\nYour message has been encrypted: $encrypted_reply_b";

# User A receives the reply
say "\n\n\n$user_a, you have received a message from $user_b.\nDecrypting message..........";
my $decrypted_reply_a = decrypt_message($encrypted_reply_b, $cipher);
say "\n\nDecrypted message: $decrypted_reply_a";

say "";
} else {
say "\nNo message sent.";
$continue = 0; # End the conversation if User B choose not to send a message
}
} else {
say "\nNo message sent.";
$continue = 0; # End the conversation if User A choose not to send a message
}
}

say "\n=============== THE CHAT HAD ENDED GOODBYE! ===============";
Sample conversation between two users

A Password Manager

Link to Project: https://github.com/Himashi-Karunathilake/Perl/tree/master/Projects/password_manager

The objective of this project is to create a simple password manager that securely stores and retrieves passwords using Perl. This project will utilize four files and eight subroutines that will be introduced as we progress.

We of course have main.pl file which is our Perl script. When we run this file, it will call the main_menu subroutine that will call the other functions in order as required. This subroutine will provide the user with a menu from which the user can choose to store a password, retrieve password or exit the menu. Supposing the user selects the option to store the password. Once the password is stored, the user will be redirected back to this menu and will only exit when the user provides the option to do so.

The main menu
Error when the user provides an invalid option
Exiting the program

If the user selects the option to store a password, the program will call upon the prompt_for_password subroutine. This subroutine will ensure that the password that the user enters is not visible on the terminal.

sub prompt_for_password {
my ($prompt) = @_;
my $password;

print $prompt;
ReadMode('noecho');
chomp($password = <STDIN>);
ReadMode('normal');
print "\n";

return $password;
}

Further, the user will be asked to enter the password twice for confirmation. If the passwords do not match, the user will be redirected to the main menu. However, if the passwords match, the program will call the store_password subroutine.

sub store_password {
my ($service, $password) = @_;
my $salt = generate_salt();
my $encrypted_password = encrypt_password($password, $salt);
my $encoded_password = encode_base64($encrypted_password);
open(my $fh, '>>', $password_file) or die "ERROR! Could not open file '$password_file': $!";
print $fh "$service:$salt:$encoded_password\n";
close($fh);
}

The store_password subroutine will then call upon the generate_salt subroutine to add a salt value to the user provided password.

sub generate_salt {
my $length = 16;
my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
my $salt = '';
$salt .= $chars[rand(@chars)] for (1..$length);
return $salt;
}

Once it has the salt value, store_password will call encrypt_password subroutine. This subroutine will use the Crypt::CBC module to create a cipher will then be used for encryption.

sub encrypt_password {
my ($password, $salt) = @_;
my $cipher = Crypt::CBC->new(-key => $encryption_key, -cipher => 'Rijndael');
my $encrypted_password = $cipher->encrypt($password . $salt);
return $encrypted_password;
}

This subroutine will also access the secret.txt file through the $encryption_key variable. The subroutine load_secret_key will be used to assign the secret key to the variable. This file stores our encryption key — in this case, a random hexadecimal key. Although we are storing our encryption key in a .txt file for the purpose of demonstration of this project, this approach is not recommended as it introduces unwarranted security risks.

secret.txt
sub load_secret_key {
my ($file) = @_;
my $key;

open my $fh, '<', $file or die "ERROR! Unable to open key file '$file': $!";
$key = <$fh>;
close($fh);

chomp($key);

unless ($key) {
die "ERROR! Encryption key not found in '$file'.";
}

return $key;
}

Once encryption is completed, the encrypted password will be written to passwords.txt which will be created if doesn’t already exist.

Error caused due to password mismatch
The password was stored successfully
passwords.txt

Now, if the user provides the option to retrieve the password, the first subroutine that will be called is the retrieve_password subroutine. This subroutine would scan the passwords.txt file for the service name for which the user requested the password and then call upon the decrypt_password subroutine which will decrypt the password using our encryption key, remove the salt value and return the decrypted password.

sub retrieve_password {
my ($service) = @_;
open(my $fh, '<', $password_file) or die "ERROR! Could not open file '$password_file': $!";
while (my $line = <$fh>) {
chomp $line;
my ($stored_service, $stored_salt, $encoded_password) = split(':', $line, 3);
if (defined $stored_service && defined $stored_salt && defined $encoded_password && $stored_service eq $service) {
close($fh);
my $encrypted_password = decode_base64($encoded_password);
return decrypt_password($encrypted_password, $stored_salt);
}
}
close($fh);
return undef;
}
sub decrypt_password {
my ($encrypted_password, $salt) = @_;
my $cipher = Crypt::CBC->new(-key => $encryption_key, -cipher => 'Rijndael');
my $decrypted_password = $cipher->decrypt($encrypted_password);
return substr($decrypted_password, 0, -length($salt));
}

Now, a new file called temp_file_for_password.txt will be created to which the contents of the retrieved password will be written to. However, for security purposes, this file will be available to the user only for 60 seconds (1 minute), after which the contents of the file will be cleared.

Error when trying to retrieve a password that does not exist
Retrieving the password successfully
temp_file_for_password.txt showcasing the retrieved information
Returning to the main menu after clearing the file
temp_file_for_password.txt after being cleared
#!/usr/bin/perl

# Objective of the Project: The following tool can be used for to securely store and retrieve passwords.

use strict;
use warnings;
use feature "say";
use Crypt::CBC;
use MIME::Base64;
use Term::ReadKey;
use Time::HiRes qw(sleep);

my $password_file = "passwords.txt"; # File containing the stored passwords
my $key_file = "secret.txt"; # File containing the secret key
my $encryption_key = load_secret_key($key_file);

sub prompt_for_password {
my ($prompt) = @_;
my $password;

print $prompt;
ReadMode('noecho');
chomp($password = <STDIN>);
ReadMode('normal');
print "\n";

return $password;
}

sub store_password {
my ($service, $password) = @_;
my $salt = generate_salt();
my $encrypted_password = encrypt_password($password, $salt);
my $encoded_password = encode_base64($encrypted_password);
open(my $fh, '>>', $password_file) or die "ERROR! Could not open file '$password_file': $!";
say $fh "$service:$salt:$encoded_password";
close($fh);
}

sub generate_salt {
my $length = 16;
my @chars = ('a'..'z', 'A'..'Z', '0'..'9');
my $salt = '';
$salt .= $chars[rand(@chars)] for (1..$length);
return $salt;
}

sub encrypt_password {
my ($password, $salt) = @_;
my $cipher = Crypt::CBC->new(-key => $encryption_key, -cipher => 'Rijndael');
my $encrypted_password = $cipher->encrypt($password . $salt);
return $encrypted_password;
}

sub load_secret_key {
my ($file) = @_;
my $key;

open my $fh, '<', $file or die "ERROR! Unable to open key file '$file': $!";
$key = <$fh>;
close($fh);

chomp($key);

unless ($key) {
die "ERROR! Encryption key not found in '$file'.";
}

return $key;
}

sub retrieve_password {
my ($service) = @_;
open(my $fh, '<', $password_file) or die "ERROR! Could not open file '$password_file': $!";
while (my $line = <$fh>) {
chomp $line;
my ($stored_service, $stored_salt, $encoded_password) = split(':', $line, 3);
if (defined $stored_service && defined $stored_salt && defined $encoded_password && $stored_service eq $service) {
close($fh);
my $encrypted_password = decode_base64($encoded_password);
return decrypt_password($encrypted_password, $stored_salt);
}
}
close($fh);
return undef;
}

sub decrypt_password {
my ($encrypted_password, $salt) = @_;
my $cipher = Crypt::CBC->new(-key => $encryption_key, -cipher => 'Rijndael');
my $decrypted_password = $cipher->decrypt($encrypted_password);
return substr($decrypted_password, 0, -length($salt));
}

sub main_menu {
while (1) {
say "\nPassword Manager Menu:";
say "1. Store a Password";
say "2. Retrieve a Password";
say "3. Exit";
print "\nEnter your choice (1/2/3): ";
my $choice = <STDIN>;
chomp $choice;

if ($choice == 1) {
say "\nStoring a password..............";
print "\nEnter the service name: ";
my $service = <STDIN>;
chomp $service;

my $password1 = prompt_for_password("Enter the password: ");
my $password2 = prompt_for_password("Confirm the password: ");

if ($password1 eq $password2) {
store_password($service, $password1);
say "\nPassword stored successfully!";
say "\nReturning to main menu.............."
} else {
say "\nERROR! Passwords do not match. Password not stored.";
say "\nReturning to main menu.............."
}
} elsif ($choice == 2) {
say "\nRetrieving a password..............";
print "\nEnter the service name to retrieve the password: ";
my $service = <STDIN>;
chomp $service;
my $password = retrieve_password($service);

if ($password) {
my $filename = "temp_file_for_password"; # Specify the filename
open(my $output_fh, '>', $filename) or die "ERROR! Could not open file '$filename': $!";
print $output_fh "Service Name: $service\n";
print $output_fh "Password: $password\n";
close($output_fh);

say "\nPassword retrieved and saved to '$filename'. This file will be cleared in 60 seconds.\n";

# Sleep for 1 minute (60 seconds) before removing the file
sleep(60);

open(my $clear_fh, '>', $filename) or die "ERROR! Could not open file '$filename': $!";
print $clear_fh "Service Name: \n";
print $clear_fh "Password: \n";
close($clear_fh);

say "\nThe file has been cleared.";
say "\nReturning to main menu..............";
} else {
say "\nERROR! Password not found for $service.";
say "\nReturning to main menu..............";
}
} elsif ($choice == 3) {
last;
} else {
say "\nInvalid choice. Please try again.";
say "\nReturning to main menu..............";
}
}
}

# Start of the program
say "=============== WELCOME TO PASSWORD MANAGER ===============";

main_menu();

say "\n=============== THANK YOU FOR USING PASSWORD MANAGER ===============";

--

--

Himashi Karunathilake
Himashi Karunathilake

Written by Himashi Karunathilake

I am a cybersecurity enthusiast and writer with a passion for demystifying complex topics. Join me as I explore the ever-evolving world of cybersecurity!

No responses yet