Go — 6

Himashi Karunathilake
11 min readJul 27, 2023
Link to Part 1: https://himashikarunathilake.medium.com/go-1-74940ce2556d
Link to Part 2: https://himashikarunathilake.medium.com/go-2-79dc2c04db26
Link to Part 3: https://himashikarunathilake.medium.com/go-3-acbfda8360ed
Link to Part 4: https://himashikarunathilake.medium.com/go-4-7b1ead499bb9
Link to Part 5: https://himashikarunathilake.medium.com/go-5-fc86e28c6e7f

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

NOTE:

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

A Password Cracker

Link to Project: https://github.com/Himashi-Karunathilake/Go/tree/master/Projects/password_cracker

The objective of this project is to design a simple program in Go that will check if a brute-force or dictionary-based password cracking can be performed on a user provided password. If the password is found in the dictionary, the user will be notified that the password currently in use in insecure and that it needs to be changed. However, if it is not found, the user will be notified that the password in use seems to be secure as it wasn’t found in the dictionary that was used.

To meet this objective, we should first find a list of passwords. For this project, I have used a list of 100,000 passwords that you too can find here: Passwords/10_million_password_list_top_100000.txt at master · berandal666/Passwords (github.com) (special thanks to the owner of this repository 😊).

Once you have a password list you want to use, you can either download it into your local folder or else access it through a GET request. In this case, I have downloaded the password file to the same workspace.

Now we need to open this password file and read its contents to a string. It is also a good practice to do a bit of error handling in this case and print the error message to the user if incurred while reading the password file.

/* Objective of the Project:
Check if a brute-force or dictionary-based password cracking can be performed on a user provided password.

NOTE: The password file that is being used was obtained from: https://github.com/berandal666/Passwords/blob/master/10_million_password_list_top_100000.txt */

package main

import (
"fmt"
"os" // Provide buffered I/O operations; previously "io/ioutils" package which is now depreciated
)

func main() {
fmt.Println("Initialization started..........")

// Open the file and read the contents into a string
filePath := "passwords_file.txt"
readContent, readError := os.ReadFile(filePath)
if readError != nil {
fmt.Println("There was an error reading the file.\n", readError)
return
}
}

Now, we need to split this string into a list of passwords and store them in a map as key-value pairs. The for loop used in for this purpose uses an underscore placeholder “_” to skip over the index of the element, as it’s not needed in this case.

/* Objective of the Project:
Check if a brute-force or dictionary-based password cracking can be performed on a user provided password.

NOTE: The password file that is being used was obtained from: https://github.com/berandal666/Passwords/blob/master/10_million_password_list_top_100000.txt */

package main

import (
"fmt"
"os" // Provide buffered I/O operations; previously "io/ioutils" package which is now depreciated
"strings"
)

func main() {
fmt.Println("Initialization started..........")

// Open the file and read the contents into a string
filePath := "passwords_file.txt"
readContent, readError := os.ReadFile(filePath)
if readError != nil {
fmt.Println("There was an error reading the file.\n", readError)
return
}

// Split the string into a list of passwords
fileContent := strings.Split(string(readContent), "\n")

// Create a map where the keys are the passwords and the values are true.
passwordMap := make(map[string]bool)

for _, password := range fileContent {
passwordMap[password] = true
}

fmt.Println("Initialization completed!")
fmt.Println()
}

Next, we need the user to enter the password that needs to be verified. When doing this, we must ensure that the password is entered in a secure manner. Therefore, we use the golang.org/x/term package to ensure that the password is not visible on the terminal when entering. Once we have the password, it needs to be converted to a string.

/* Objective of the Project:
Check if a brute-force or dictionary-based password cracking can be performed on a user provided password.

NOTE: The password file that is being used was obtained from: https://github.com/berandal666/Passwords/blob/master/10_million_password_list_top_100000.txt */

package main

import (
"fmt"
"os" // Provide buffered I/O operations; previously "io/ioutils" package which is now depreciated
"strings"
"syscall"

"golang.org/x/term"
)

func main() {
fmt.Println("Initialization started..........")

// Open the file and read the contents into a string
filePath := "passwords_file.txt"
readContent, readError := os.ReadFile(filePath)
if readError != nil {
fmt.Println("There was an error reading the file.\n", readError)
return
}

// Split the string into a list of passwords
fileContent := strings.Split(string(readContent), "\n")

// Create a map where the keys are the passwords and the values are true.
passwordMap := make(map[string]bool)

for _, password := range fileContent {
passwordMap[password] = true
}

fmt.Println("Initialization completed!")
fmt.Println()

// Obtain the password that needs to be verified from the user
userPassword := ""

fmt.Print("Please enter the password that you need to verify: ")

passwordBytes, passwordError := term.ReadPassword(int(syscall.Stdin))
if passwordError != nil {
fmt.Println("\nThere was an error reading the password.\n", passwordError)
return
}

fmt.Println()

// Convert the password bytes to a string
userPassword = string(passwordBytes)
}

Now, we will start the dictionary-based attack. Here, we would compare our user given password with all 100,000 passwords in our map. If found, we can consider that the user given password is insecure and if not, we can consider the user given password to be secure for the time being.

/* Objective of the Project:
Check if a brute-force or dictionary-based password cracking can be performed on a user provided password.

NOTE: The password file that is being used was obtained from: https://github.com/berandal666/Passwords/blob/master/10_million_password_list_top_100000.txt */

package main

import (
"fmt"
"os" // Provide buffered I/O operations; previously "io/ioutils" package which is now depreciated
"strings"
"syscall"

"golang.org/x/term"
)

func main() {
fmt.Println("Initialization started..........")

// Open the file and read the contents into a string
filePath := "passwords_file.txt"
readContent, readError := os.ReadFile(filePath)
if readError != nil {
fmt.Println("There was an error reading the file.\n", readError)
return
}

// Split the string into a list of passwords
fileContent := strings.Split(string(readContent), "\n")

// Create a map where the keys are the passwords and the values are true.
passwordMap := make(map[string]bool)

for _, password := range fileContent {
passwordMap[password] = true
}

fmt.Println("Initialization completed!")
fmt.Println()

// Obtain the password that needs to be verified from the user
userPassword := ""

fmt.Print("Please enter the password that you need to verify: ")

passwordBytes, passwordError := term.ReadPassword(int(syscall.Stdin))
if passwordError != nil {
fmt.Println("\nThere was an error reading the password.\n", passwordError)
return
}

fmt.Println()

// Convert the password bytes to a string
userPassword = string(passwordBytes)

// Start the dictionary-based attack
fmt.Println("\nStarting dictionary-based attack..........")
fmt.Println("Please wait while the attack completes..........")

var exist bool

for password := range passwordMap {
if password == userPassword {
fmt.Println("Dictionary-based attack completed!\n\nFetching your results..........")
fmt.Println("\nOh no! The password you provided was found! Please consider using another, more secure password in your system.")
exist = true
break
} else {
exist = false
}
}

if !exist {
fmt.Println("Dictionary-based attack completed!\n\nFetching your results..........")
fmt.Println("\nCongratulations! The password you entered was not found. The password currently in use is secure.")
}

fmt.Println("\nExiting programme!")
}
The password entered exists in the map of passwords used
The password entered does not exist in the map of passwords used

A File Encrypting / Decrypting Tool

Link to Project: https://github.com/Himashi-Karunathilake/Go/tree/master/Projects/file_encrypt_decrypt

The objective of this script is to create a tool that allows users to encrypt and decrypt a file using a passphrase. In this script, the user can provide a file that they need to encrypt and a passphrase of their choice. This tool will then make a copy of the original file and encrypt it (so that the original file is not corrupted in case something goes wrong). If the user needs to decrypt this encrypted file, they can run this script again and provide the encrypted file as the input file and the same passphrase that was used during encryption. This will once again create a copy of the encrypted file (so that the current encrypted file will not be corrupted in case something goes wrong) and decrypt it.

Given below is the complete code for this tool:

/* Objective of the Project:
The following tool allows users to encrypt and decrypt a file using a passphrase. */

package main

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"os"
"syscall"

"golang.org/x/term"
)

// Declare constants
const (
ivSize = 12 // Size of the IV block is 12 bytes (96 bits)
keySize = 16 // The key size is 16 bytes (128 bits)
)

// Function to encrypt a file
func encryptFile(inputFile, outputFile string, passphrase []byte) error {
// Read the input file
readFile, readError := os.ReadFile(inputFile)
if readError != nil {
fmt.Println("There was an error reading the file.\n", readError)
return readError
}

// Create an AES cipher block using the provided passphrase
cipherBlock, cipherError := aes.NewCipher(passphrase)
if cipherError != nil {
fmt.Println("There was an error creating an AES cipher block using the passphrase.\n", cipherError)
return cipherError
}

// Generate a random initialization vector (IV)
iv := make([]byte, ivSize)
if _, ivError := rand.Read(iv); ivError != nil {
fmt.Println("There was an error generating the initialization vector.\n", ivError)
return ivError
}

// Create a new AES cipher
aesCipher, aesError := cipher.NewGCM(cipherBlock)
if aesError != nil {
fmt.Println("There was an error creating an AES cipher.\n", aesError)
return aesError
}

// Encrypt the input data
encryptedData := aesCipher.Seal(nil, iv, readFile, nil)

// Write the encrypted data to the output file along with the IV
output, outputError := os.Create(outputFile)
if outputError != nil {
fmt.Println("There was an error writing to the output file.\n", outputError)
return outputError
}
defer output.Close()

output.Write(iv)
output.Write(encryptedData)

fmt.Println("***** File encrypted successfully! *****")
fmt.Println()

return nil
}

// Function to decrypt a file
func decryptFile(inputFile, outputFile string, passphrase []byte) error {
// Read the encrypted file
readFile, readError := os.ReadFile(inputFile)
if readError != nil {
fmt.Println("There was an error reading the file.\n", readError)
return readError
}

// Create an AES cipher block using the provided passphrase
cipherBlock, cipherError := aes.NewCipher(passphrase)
if cipherError != nil {
fmt.Println("There was an error creating an AES cipher block using the passphrase.\n", cipherError)
return cipherError
}

// Extract IV from the input data (first block)
iv := readFile[:ivSize]
encryptedData := readFile[ivSize:]

// Create a new AES cipher
aesCipher, aesError := cipher.NewGCM(cipherBlock)
if aesError != nil {
fmt.Println("There was an error creating an AES cipher.\n", aesError)
return aesError
}

// Decrypt the data
decryptData, decryptError := aesCipher.Open(nil, iv, encryptedData, nil)
if decryptError != nil {
fmt.Println("There was an error decrypting the data.\n", decryptError)
return decryptError
}

// Write the decrypted data to the output file
output, outputError := os.Create(outputFile)
if outputError != nil {
fmt.Println("There was an error writing to the output file.\n", outputError)
return outputError
}
defer output.Close()

output.Write(decryptData)

fmt.Println("***** File decrypted successfully! *****")
fmt.Println()

return nil
}

// Main function
func main() {
fmt.Println("***** WELCOME TO AES FILE ENCRYPT / DECRYPT *****")
fmt.Println()

var choice int
var inputFile, outputFileName string

fmt.Print("What would you like to do?\nEnter 1 for file encryption.\nEnter 2 for file decryption.\n\nI would like to: ")
fmt.Scan(&choice)

if choice == 1 {
// Calling the file encryption function
fmt.Println("\nStarting file encryption.....")
fmt.Println()

fmt.Print("Please provide the name and / or location of the file to be encrypted (with the file extension - e.g., sampleFile.txt): ")
fmt.Scan(&inputFile)

fmt.Print("Input file noted!\n\nPlease provide the name and / or location for the encrypted file (with the file extension - e.g., sampleEncryptedFile.txt): ")
fmt.Scan(&outputFileName)

fmt.Print("Output file noted!\n\nPlease enter a passphrase to be used for encryption: ")

passphrase, passphraseError := term.ReadPassword(int(syscall.Stdin))
if passphraseError != nil {
fmt.Println("\nThere was an error reading the password.\n", passphraseError)
return
}

fmt.Println()
fmt.Println()

// Truncate or pad the passphrase to 16 bytes (128 bits)
if len(passphrase) < keySize {
padding := make([]byte, keySize-len(passphrase))
passphrase = append(passphrase, padding...)
} else if len(passphrase) > keySize {
passphrase = passphrase[:keySize]
} else {
return
}

encryptError := encryptFile(inputFile, outputFileName, passphrase)
if encryptError != nil {
fmt.Println("There was an error encrypting the file.\n", encryptError)
return
}
} else if choice == 2 {
// Calling the file decryption function
fmt.Println("\nStarting file decryption.....")
fmt.Println()

fmt.Print("Please provide the name and / or location of the file to be decrypted (with the file extension - e.g., sampleEncryptedFile.txt): ")
fmt.Scan(&inputFile)

fmt.Print("Input file noted!\n\nPlease provide the name and / or location for the decrypted file (with the file extension - e.g., sampleDecryptedFile.txt): ")
fmt.Scan(&outputFileName)

fmt.Print("Output file noted!\n\nPlease enter a passphrase to be used for decryption: ")

passphrase, passphraseError := term.ReadPassword(int(syscall.Stdin))
if passphraseError != nil {
fmt.Println("\nThere was an error reading the password.\n", passphraseError)
return
}

fmt.Println()
fmt.Println()

// Truncate or pad the passphrase to 16 bytes (128 bits)
if len(passphrase) < keySize {
padding := make([]byte, keySize-len(passphrase))
passphrase = append(passphrase, padding...)
} else if len(passphrase) > keySize {
passphrase = passphrase[:keySize]
} else {
return
}

decryptError := decryptFile(inputFile, outputFileName, passphrase)
if decryptError != nil {
fmt.Println("There was an error decrypting the file.\n", decryptError)
return
}
} else {
fmt.Println("\nERROR! Invalid Choice! Exiting program.....")
fmt.Println()
}

fmt.Println("Thank you for using AES File Encrypt / Decrypt! Have a nice day!")
fmt.Println()
}

In this code, we define two constants. The ivSize constant represents the size of the Initialization Vector (IV) used in the AES-GCM encryption. The IV size is set to 12 bytes which is the recommended sizes for AES-GCM. The constant keySize represents the size of the encryption key used for AES. The key size is set for 16 bytes which is the key size for AES-128 encryption.

AES        Advanced Encryption Standard is a widely used symmetric 
encryption algorithm for securing data.

AES-GCM Galois / Counter Mode is a specific mode of operation for the
AES encryption algorithm that provides both confidentiality
and authenticity.

AES-128 128 refers to the key size used in the AES encryption algorithm.
// Declare constants
const (
ivSize = 12 // Size of the IV block is 12 bytes (96 bits)
keySize = 16 // The key size is 16 bytes (128 bits)
)

First, let’s try encrypting a file. Let’s name this file as input_file.txt and keep it in the same directory as the script.

input_file.txt

Let’s now run the script and provide the option to encrypt a file.

input_file.txt was encrypted successfully
encrypted_file.txt

Let’s now run the script for decrypting an encrypted file by providing the same passphrase that was used to encrypt it.

encrypted_file.txt was decrypted successfully
decrypted_file.txt

Here we can see that both the input_file.txt and decrypted_file.txt have the same content within, meaning that our tool works as expected.

--

--

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!