diff --git a/src/client/utils/files.go b/src/client/utils/files.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f0f7fd6488c2bacc319f0143262084455e157d0
--- /dev/null
+++ b/src/client/utils/files.go
@@ -0,0 +1,84 @@
+package utils
+
+import (
+	"archive/tar"
+	"bytes"
+	"compress/gzip"
+	"errors"
+	"io"
+	"io/fs"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/google/uuid"
+)
+
+// Creates a tar.gz archive of dir and all its files and subdirectories.
+// Note: dir can also be a file.
+// Source code slightly modified from: https://gist.github.com/mimoo/25fc9716e0f1353791f5908f94d6e726
+func TarGzDir(dir, archive string) error {
+	_, err := os.Stat(dir)
+	if errors.Is(err, fs.ErrNotExist) {
+		return errors.New("Error: \"" + dir + "\" does not exist")
+	}
+
+	var buf bytes.Buffer
+
+	gzWriter := gzip.NewWriter(&buf)
+	tarWriter := tar.NewWriter(gzWriter)
+
+	// Walks through every file in the directory
+	filepath.Walk(dir, func(file string, fi os.FileInfo, err error) error {
+		header, err := tar.FileInfoHeader(fi, file)
+		if err != nil {
+			return err
+		}
+
+		fileRel := strings.TrimPrefix(file, dir)
+		fileRel = filepath.Join(filepath.Base(dir), fileRel)
+		header.Name = filepath.ToSlash(fileRel)
+
+		if err := tarWriter.WriteHeader(header); err != nil {
+			return err
+		}
+		// if not a dir, write file content
+		if !fi.IsDir() {
+			data, err := os.Open(file)
+			if err != nil {
+				return err
+			}
+			if _, err := io.Copy(tarWriter, data); err != nil {
+				return err
+			}
+		}
+		return nil
+	})
+
+	if err := tarWriter.Close(); err != nil {
+		return err
+	}
+	if err := gzWriter.Close(); err != nil {
+		return err
+	}
+
+	fileToWrite, err := os.OpenFile(archive, os.O_CREATE|os.O_RDWR, os.FileMode(0750))
+	if err != nil {
+		return err
+	}
+	if _, err := io.Copy(fileToWrite, &buf); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func GetRandomTempFilename() (string, error) {
+	tempDir := os.TempDir()
+	uuid, err := uuid.NewRandom()
+	if err != nil {
+		return "", errors.New("Failed creating random UUID: " + err.Error())
+	}
+	randName := "temp_" + uuid.String()
+	return filepath.Join(tempDir, randName), nil
+}
diff --git a/src/client/utils/strings.go b/src/client/utils/strings.go
new file mode 100644
index 0000000000000000000000000000000000000000..854d18605f4623c605c13acbae4628db587f24a8
--- /dev/null
+++ b/src/client/utils/strings.go
@@ -0,0 +1,48 @@
+package utils
+
+import (
+	"fmt"
+	"io/ioutil"
+	"net/mail"
+	"strings"
+)
+
+func IsEmail(email string) bool {
+	_, err := mail.ParseAddress(email)
+	return err == nil
+}
+
+// Convert a string of USB devices of the form "1fc9:001d,067b:2303"
+// into a slice of string where each element is a string of the form "1fc9:001d".
+// Returns an empty slice if the input string is "none".
+func Str2UsbDevices(s string) []string {
+	usbDevs := []string{}
+	if s != "none" {
+		devs := strings.Split(s, ",") // Extracts USB devices
+		for _, dev := range devs {
+			usbDevs = append(usbDevs, dev)
+		}
+	}
+	return usbDevs
+}
+
+// Returns the content of file as a string
+func FileToString(file string) (string, error) {
+	content, err := ioutil.ReadFile(file)
+	if err != nil {
+		return "", err
+	}
+	return string(content), nil
+}
+
+// Removes an element from a string array at a given index
+func RemoveArgAtIndex(slice []string, index int) []string {
+	return append(slice[:index], slice[index+1:]...)
+}
+
+// TODO: better way of appending a portable "new line" to a string
+func AppendNewLine(s string) string {
+	var newLine string
+	newLine = fmt.Sprintln(newLine, "")
+	return s + newLine
+}
diff --git a/src/common/utils/files.go b/src/common/utils/files.go
new file mode 100644
index 0000000000000000000000000000000000000000..729503afc05dc11609c0eb56374d0095a4e16865
--- /dev/null
+++ b/src/common/utils/files.go
@@ -0,0 +1,12 @@
+package utils
+
+import (
+	"errors"
+	"os"
+)
+
+// Returns true if the specified file exists, false otherwise.
+func FileExists(filename string) bool {
+	_, err := os.Stat(filename)
+	return !errors.Is(err, os.ErrNotExist)
+}
diff --git a/src/server/utils/files.go b/src/server/utils/files.go
new file mode 100644
index 0000000000000000000000000000000000000000..67351766868bc956970d6ff447e3ecdcefe1615a
--- /dev/null
+++ b/src/server/utils/files.go
@@ -0,0 +1,52 @@
+package utils
+
+import (
+	"io"
+	"os"
+	"path/filepath"
+)
+
+// Returns the list of subdirectories present in dir.
+func GetSubDirs(dir string) ([]string, error) {
+	subDirs := []string{}
+
+	currentDir, err := os.Open(dir)
+	if err != nil {
+		return nil, err
+	}
+
+	// Retrieves all files entries in the directory (0 = all files in the directory).
+	files, err := currentDir.Readdir(0)
+	if err != nil {
+		currentDir.Close()
+		return nil, err
+	}
+
+	currentDir.Close()
+
+	// Loop over file entries
+	for _, f := range files {
+		if f.IsDir() {
+			subDirs = append(subDirs, filepath.Join(dir, f.Name()))
+		}
+	}
+
+	return subDirs, nil
+}
+
+func CopyFiles(source, dest string) error {
+	src, err := os.Open(source)
+	if err != nil {
+		return err
+	}
+	defer src.Close()
+
+	dst, err := os.Create(dest)
+	if err != nil {
+		return err
+	}
+	defer dst.Close()
+	_, err = io.Copy(dst, src)
+
+	return err
+}
diff --git a/src/server/utils/network.go b/src/server/utils/network.go
new file mode 100644
index 0000000000000000000000000000000000000000..b0164d9c2cbf0e11619ee4dc5f95c8a7212c7d84
--- /dev/null
+++ b/src/server/utils/network.go
@@ -0,0 +1,35 @@
+package utils
+
+import (
+	"math/rand"
+	"net"
+	"strconv"
+)
+
+// Returns a random MAC address as a string (e.g. "b3:30:49:f8:d0:4b").
+func RandMacAddress() (string, error) {
+	buf := make([]byte, 6)
+	_, err := rand.Read(buf)
+	if err != nil {
+		return "", err
+	}
+
+	// Sets unicast mode (bit 0 to 0) and locally administered addresses (bit 1 to 1)
+	// For more details, see: https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local_(U/L_bit)
+	buf[0] &= 0xFE
+	buf[0] |= 2
+
+	var mac net.HardwareAddr
+	mac = append(mac, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5])
+	return mac.String(), nil
+}
+
+// Returns true if the specified TCP port is available, false otherwise.
+func IsPortAvailable(port int) bool {
+	ln, err := net.Listen("tcp", ":"+strconv.Itoa(port))
+	if err != nil {
+		return false
+	}
+	ln.Close()
+	return true
+}
diff --git a/src/server/utils/password.go b/src/server/utils/password.go
index fdf7655c8d2e1824892023ccc0f4b25db72b69b2..90c6dabfd37e398dccb7c4b1eb7b0b51ce91c5ab 100644
--- a/src/server/utils/password.go
+++ b/src/server/utils/password.go
@@ -2,6 +2,7 @@ package utils
 
 import (
 	"github.com/sethvargo/go-password/password"
+	"golang.org/x/crypto/bcrypt"
 )
 
 // Custom password generator with the following characters removed: 0, O, l, I
@@ -10,3 +11,10 @@ var CustomPwd, _ = password.NewGenerator(&password.GeneratorInput{
 	UpperLetters: "ABCDEFGHJKLMNPQRSTUVWXYZ",
 	Digits:       "123456789",
 })
+
+// Returns a hash of the "clear" password passed in argument.
+func HashPassword(pwd string) string {
+	// Uses bcrypt to hash the password.
+	hashedPwd, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
+	return string(hashedPwd)
+}
diff --git a/src/server/utils/random.go b/src/server/utils/random.go
new file mode 100644
index 0000000000000000000000000000000000000000..85942a09844daeace7a5019312dd742533547e99
--- /dev/null
+++ b/src/server/utils/random.go
@@ -0,0 +1,16 @@
+package utils
+
+import (
+	"math/rand"
+	"time"
+)
+
+// Initializes the random number generator.
+func RandInit() {
+	rand.Seed(time.Now().UnixNano())
+}
+
+// Returns an int in the range [min,max] (both inclusive).
+func Rand(min, max int) int {
+	return rand.Intn(max-min+1) + min
+}