diff --git a/cli/README.md b/cli/README.md
new file mode 100644
index 0000000..7d6b35e
--- /dev/null
+++ b/cli/README.md
@@ -0,0 +1,5 @@
+# CLI
+Work in progress
+
+# 2. Install Go
+If you don't have Go installed, download it from here or install it from your package manager (`apt install golang-go`). The latest version of Go is recommended, although you may fall back to Go 1.19 should any issues arise in the future.
\ No newline at end of file
diff --git a/cli/picocrypt/go.mod b/cli/picocrypt/go.mod
new file mode 100644
index 0000000..653f501
--- /dev/null
+++ b/cli/picocrypt/go.mod
@@ -0,0 +1,10 @@
+module github.com/HACKERALERT/Picocrypt/cli/picocrypt
+
+go 1.17
+
+require (
+ github.com/HACKERALERT/crypto v0.0.0-20220905152506-aa0dd62d8f67
+ github.com/HACKERALERT/infectious v0.0.0-20220905152109-2c37b99f37ff
+)
+
+require github.com/HACKERALERT/sys v0.0.0-20220905150735-46e319fb60c9 // indirect
diff --git a/cli/picocrypt/go.sum b/cli/picocrypt/go.sum
new file mode 100644
index 0000000..241eb57
--- /dev/null
+++ b/cli/picocrypt/go.sum
@@ -0,0 +1,6 @@
+github.com/HACKERALERT/crypto v0.0.0-20220905152506-aa0dd62d8f67 h1:4WfPIopYjvBjyDg0IET7mEj32kkihLmvFgwCOmldqK8=
+github.com/HACKERALERT/crypto v0.0.0-20220905152506-aa0dd62d8f67/go.mod h1:qiHCxMDsCxX4QhXd3kDYWiNOR/DZQZ7nYO/f2OgWst0=
+github.com/HACKERALERT/infectious v0.0.0-20220905152109-2c37b99f37ff h1:ertDhqhixxQJJPJIpn4wmSVs2fQ3tlSDNuiZ7jHCvEs=
+github.com/HACKERALERT/infectious v0.0.0-20220905152109-2c37b99f37ff/go.mod h1:GwBVHbiXRUUciGfKWwm4GCL8FvMZBLHrQq23UXW/CU8=
+github.com/HACKERALERT/sys v0.0.0-20220905150735-46e319fb60c9 h1:raqLJhvqDGk9L4dnJnO0tTV5Lyba2jhQcIHEle+o1dM=
+github.com/HACKERALERT/sys v0.0.0-20220905150735-46e319fb60c9/go.mod h1:I4esFWbCYc37CXVb+3qJHJiU43NGkLf66kNV/NDnXcU=
diff --git a/cli/picocrypt/main.go b/cli/picocrypt/main.go
new file mode 100644
index 0000000..ffb25fa
--- /dev/null
+++ b/cli/picocrypt/main.go
@@ -0,0 +1,229 @@
+package main
+
+import (
+ "bytes"
+ "crypto/rand"
+ "flag"
+ "fmt"
+ "os"
+ "strconv"
+ "strings"
+
+ "github.com/HACKERALERT/crypto/argon2"
+ "github.com/HACKERALERT/crypto/blake2b"
+ "github.com/HACKERALERT/crypto/chacha20"
+ "github.com/HACKERALERT/crypto/hkdf"
+ "github.com/HACKERALERT/crypto/sha3"
+ "github.com/HACKERALERT/infectious"
+)
+
+var MiB = 1 << 20
+var GiB = 1 << 30
+var rs5, _ = infectious.NewFEC(5, 15)
+var rs16, _ = infectious.NewFEC(16, 48)
+var rs24, _ = infectious.NewFEC(24, 72)
+var rs32, _ = infectious.NewFEC(32, 96)
+var rs64, _ = infectious.NewFEC(64, 192)
+
+func work(filename string, password string) int {
+ var salt []byte
+ var hkdfSalt []byte
+ var nonce []byte
+ var keyHash []byte
+ var keyHashRef []byte
+ var authTag []byte
+
+ fin, err := os.Open(filename)
+ if err != nil {
+ fmt.Println("Couldn't open input file.")
+ return 1
+ }
+ defer fin.Close()
+
+ var fout *os.File
+ if strings.HasSuffix(filename, ".pcv") {
+ fout, err = os.Create(strings.TrimSuffix(filename, ".pcv"))
+ } else {
+ fout, err = os.Create(filename + ".pcv")
+ }
+ if err != nil {
+ fmt.Println("Couldn't create output file.")
+ return 1
+ }
+ defer fout.Close()
+
+ if !strings.HasSuffix(filename, ".pcv") {
+ salt = make([]byte, 16)
+ hkdfSalt = make([]byte, 32)
+ nonce = make([]byte, 24)
+ rand.Read(salt)
+ rand.Read(hkdfSalt)
+ rand.Read(nonce)
+ fout.Write(rsEncode(rs5, []byte("v1.32")))
+ fout.Write(rsEncode(rs5, []byte("00000")))
+ fout.Write(rsEncode(rs5, make([]byte, 5)))
+ fout.Write(rsEncode(rs16, salt))
+ fout.Write(rsEncode(rs32, hkdfSalt))
+ fout.Write(rsEncode(rs16, make([]byte, 16)))
+ fout.Write(rsEncode(rs24, nonce))
+ fout.Write(make([]byte, 480))
+ } else {
+ errs := make([]error, 7)
+ comments := make([]byte, 30)
+ fin.Read(comments)
+ comments, errs[0] = rsDecode(rs5, comments[15:])
+ length, _ := strconv.Atoi(string(comments))
+ fin.Read(make([]byte, length*3))
+ flags := make([]byte, 15)
+ fin.Read(flags)
+ flags, errs[1] = rsDecode(rs5, flags)
+ salt = make([]byte, 48)
+ fin.Read(salt)
+ salt, errs[2] = rsDecode(rs16, salt)
+ hkdfSalt = make([]byte, 96)
+ fin.Read(hkdfSalt)
+ hkdfSalt, errs[3] = rsDecode(rs32, hkdfSalt)
+ fin.Read(make([]byte, 48))
+ nonce = make([]byte, 72)
+ fin.Read(nonce)
+ nonce, errs[4] = rsDecode(rs24, nonce)
+ keyHashRef = make([]byte, 192)
+ fin.Read(keyHashRef)
+ keyHashRef, errs[5] = rsDecode(rs64, keyHashRef)
+ fin.Read(make([]byte, 96))
+ authTag = make([]byte, 192)
+ fin.Read(authTag)
+ authTag, errs[6] = rsDecode(rs64, authTag)
+ for _, err := range errs {
+ if err != nil {
+ fmt.Println("The header is corrupted.")
+ return 1
+ }
+ }
+ if flags[0]+flags[1]+flags[3] > 0 {
+ fmt.Println("Unsupported volume.")
+ return 1
+ }
+ }
+
+ key := argon2.IDKey([]byte(password), salt, 4, 1<<20, 4, 32)
+ tmp := sha3.New512()
+ tmp.Write(key)
+ keyHash = tmp.Sum(nil)
+ if strings.HasSuffix(filename, ".pcv") && !bytes.Equal(keyHash, keyHashRef) {
+ fmt.Println("Incorrect password.")
+ return 1
+ }
+
+ counter := 0
+ chacha, _ := chacha20.NewUnauthenticatedCipher(key, nonce)
+ subkey := make([]byte, 32)
+ hkdf := hkdf.New(sha3.New256, key, hkdfSalt, nil)
+ hkdf.Read(subkey)
+ mac, _ := blake2b.New512(subkey)
+ hkdf.Read(make([]byte, 32))
+
+ for {
+ src := make([]byte, MiB)
+ size, err := fin.Read(src)
+ if err != nil {
+ break
+ }
+ src = src[:size]
+ dst := make([]byte, len(src))
+
+ if !strings.HasSuffix(filename, ".pcv") {
+ chacha.XORKeyStream(dst, src)
+ mac.Write(dst)
+ } else {
+ mac.Write(src)
+ chacha.XORKeyStream(dst, src)
+ }
+ fout.Write(dst)
+
+ counter += MiB
+ if counter >= 60*GiB {
+ nonce = make([]byte, 24)
+ hkdf.Read(nonce)
+ chacha, _ = chacha20.NewUnauthenticatedCipher(key, nonce)
+ hkdf.Read(make([]byte, 16))
+ counter = 0
+ }
+ }
+
+ if !strings.HasSuffix(filename, ".pcv") {
+ fout.Seek(309, 0)
+ fout.Write(rsEncode(rs64, keyHash))
+ fout.Write(rsEncode(rs32, make([]byte, 32)))
+ fout.Write(rsEncode(rs64, mac.Sum(nil)))
+ } else {
+ if !bytes.Equal(mac.Sum(nil), authTag) {
+ fmt.Println("The file has been modified.")
+ return 1
+ }
+ }
+
+ fmt.Println("Operation successful.")
+ return 0
+}
+
+func rsEncode(rs *infectious.FEC, data []byte) []byte {
+ res := make([]byte, rs.Total())
+ rs.Encode(data, func(s infectious.Share) {
+ res[s.Number] = s.Data[0]
+ })
+ return res
+}
+
+func rsDecode(rs *infectious.FEC, data []byte) ([]byte, error) {
+ tmp := make([]infectious.Share, rs.Total())
+ for i := 0; i < rs.Total(); i++ {
+ tmp[i].Number = i
+ tmp[i].Data = []byte{data[i]}
+ }
+ res, err := rs.Decode(nil, tmp)
+ if err != nil {
+ return data[:rs.Total()/3], err
+ }
+ return res, nil
+}
+
+func main() {
+ flag.Usage = func() { fmt.Println("Usage: picocrypt -p password ") }
+ password := flag.String("p", "", "")
+ flag.Parse()
+ filename := flag.Arg(0)
+
+ if filename == "" || *password == "" || flag.Arg(1) != "" {
+ flag.Usage()
+ os.Exit(1)
+ }
+ if _, err := os.Stat(filename); err != nil {
+ fmt.Println("Input file not found.")
+ os.Exit(1)
+ }
+ if stat, _ := os.Stat(filename); stat.IsDir() {
+ fmt.Println("Directories are not supported.")
+ os.Exit(1)
+ }
+ if !strings.HasSuffix(filename, ".pcv") {
+ if _, err := os.Stat(filename + ".pcv"); err == nil {
+ fmt.Println("Output already exists.")
+ os.Exit(1)
+ }
+ } else {
+ if _, err := os.Stat(strings.TrimSuffix(filename, ".pcv")); err == nil {
+ fmt.Println("Output already exists.")
+ os.Exit(1)
+ }
+ }
+
+ if work(filename, *password) != 0 {
+ if !strings.HasSuffix(filename, ".pcv") {
+ os.Remove(filename + ".pcv")
+ } else {
+ os.Remove(strings.TrimSuffix(filename, ".pcv"))
+ }
+ os.Exit(1)
+ }
+}