From 7a8a9af5e0eb9e42516e395bd547245b92f640a8 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Sat, 1 Jun 2024 14:35:01 -0400
Subject: [PATCH] Initial commit
---
.github/workflows/codeql-analysis.yml | 32 ++
picocrypt/README.md | 34 ++
picocrypt/go.mod | 17 +
picocrypt/go.sum | 29 ++
picocrypt/main.go | 676 ++++++++++++++++++++++++++
5 files changed, 788 insertions(+)
create mode 100644 .github/workflows/codeql-analysis.yml
create mode 100644 picocrypt/README.md
create mode 100644 picocrypt/go.mod
create mode 100644 picocrypt/go.sum
create mode 100644 picocrypt/main.go
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..8fc41bc
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,32 @@
+name: "CodeQL"
+on:
+ push:
+ paths:
+ - "picocrypt/*.go"
+ - "picocrypt/go.mod"
+ - "picocrypt/go.sum"
+ pull_request:
+ branches: [ main ]
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ['go']
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: ${{ matrix.language }}
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
diff --git a/picocrypt/README.md b/picocrypt/README.md
new file mode 100644
index 0000000..e4463d9
--- /dev/null
+++ b/picocrypt/README.md
@@ -0,0 +1,34 @@
+# Installation
+If you don't have Go installed, download it from here or install it from your package manager. Then, run the command below:
+```
+go install github.com/HACKERALERT/Picocrypt/cli/v2/picocrypt@latest
+```
+You should now be able to run `picocrypt` in your terminal. If not, run `export PATH=$PATH:$(go env GOPATH)/bin` and try again.
+# Usage
+```
+C:\Users\Evan>picocrypt
+Usage: picocrypt [ ...]
+Items: can be files, folders, or globs
+Flags:
+ -f (decryption) attempt to fix corruption
+ -k (decryption) keep output even if corrupted
+ -p (encryption) use paranoid mode
+ -r (encryption) encode with Reed-Solomon
+```
+## Examples
+To encrypt a single file:
+```
+picocrypt secret.pdf
+```
+To encrypt all files in the current working directory:
+```
+picocrypt *
+```
+To encrypt all PNGs and JPGs with paranoid mode and Reed-Solomon:
+```
+picocrypt -p -r *.png *.jpg
+```
+To decrypt a volume:
+```
+picocrypt volume.pcv
+```
diff --git a/picocrypt/go.mod b/picocrypt/go.mod
new file mode 100644
index 0000000..43ea7d0
--- /dev/null
+++ b/picocrypt/go.mod
@@ -0,0 +1,17 @@
+module github.com/HACKERALERT/Picocrypt/cli/v2/picocrypt
+
+go 1.22.2
+
+require (
+ github.com/HACKERALERT/infectious v0.0.0-20240424200929-b9ce72346a19
+ github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66
+ github.com/schollz/progressbar/v3 v3.14.2
+ golang.org/x/crypto v0.22.0
+ golang.org/x/term v0.19.0
+)
+
+require (
+ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
+ golang.org/x/sys v0.19.1-0.20240416221847-9a28524796a5 // indirect
+)
diff --git a/picocrypt/go.sum b/picocrypt/go.sum
new file mode 100644
index 0000000..aa9a423
--- /dev/null
+++ b/picocrypt/go.sum
@@ -0,0 +1,29 @@
+github.com/HACKERALERT/infectious v0.0.0-20240424200929-b9ce72346a19 h1:C5t561XXXRJvdiluejbka36n+YaOB4XJuQIo+25hL1k=
+github.com/HACKERALERT/infectious v0.0.0-20240424200929-b9ce72346a19/go.mod h1:bTnpEk9zNS1sVKg5TRvLkuSEGVqH0+LRfcMurPtcJvY=
+github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66 h1:YDpFq+y6mRcu97rn/rhYg8u8FdeO0wzTuLgM2gVkA+c=
+github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66/go.mod h1:d/+9q3sIxtIyOgHNgFGr3yGBKKVn5h3vL4hV1qlmoLs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
+github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyMFIEa99ks=
+github.com/schollz/progressbar/v3 v3.14.2/go.mod h1:aQAZQnhF4JGFtRJiw/eobaXpsqpVQAftEQ+hLGXaRc4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.1-0.20240416221847-9a28524796a5 h1:0exPaeAtAlmNHCcRJc+hETS3/TcMV+yjoHhlp4+Ff3E=
+golang.org/x/sys v0.19.1-0.20240416221847-9a28524796a5/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
+golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
diff --git a/picocrypt/main.go b/picocrypt/main.go
new file mode 100644
index 0000000..b9c38c0
--- /dev/null
+++ b/picocrypt/main.go
@@ -0,0 +1,676 @@
+package main
+
+import (
+ "archive/zip"
+ "bytes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/rand"
+ "flag"
+ "fmt"
+ "hash"
+ "io"
+ "math"
+ "os"
+ "os/signal"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/HACKERALERT/infectious"
+ "github.com/HACKERALERT/serpent"
+ "github.com/schollz/progressbar/v3"
+ "golang.org/x/crypto/argon2"
+ "golang.org/x/crypto/blake2b"
+ "golang.org/x/crypto/chacha20"
+ "golang.org/x/crypto/hkdf"
+ "golang.org/x/crypto/sha3"
+ "golang.org/x/term"
+)
+
+var f *bool
+var k *bool
+var p *bool
+var r *bool
+var mode string
+
+func parse() int {
+ flag.Usage = func() {
+ fmt.Println("Usage: picocrypt [ ...]")
+ fmt.Println("Items: can be files, folders, or globs")
+ fmt.Println("Flags:")
+ flag.PrintDefaults()
+ os.Exit(1)
+ }
+ f = flag.Bool("f", false, "(decryption) attempt to fix corruption")
+ k = flag.Bool("k", false, "(decryption) keep output even if corrupted")
+ p = flag.Bool("p", false, "(encryption) use paranoid mode")
+ r = flag.Bool("r", false, "(encryption) encode with Reed-Solomon")
+ flag.Parse()
+
+ if flag.NArg() == 0 {
+ flag.Usage()
+ }
+ for _, v := range flag.Args() {
+ if strings.HasPrefix(v, "-") {
+ fmt.Println("Flags must be provided before arguments!")
+ return 1
+ }
+ }
+
+ if flag.NArg() == 1 {
+ if strings.HasSuffix(flag.Arg(0), ".pcv") {
+ mode = "decrypt"
+ } else {
+ mode = "encrypt"
+ }
+ } else {
+ mode = "encrypt"
+ for _, v := range flag.Args() {
+ if strings.HasSuffix(v, ".pcv") {
+ fmt.Println("Multiple items must not contain volumes.")
+ return 1
+ }
+ }
+ }
+
+ return 0
+}
+
+var password []byte
+var confirmp []byte
+var err error
+
+func auth() int {
+ if mode == "encrypt" {
+ fmt.Print("Password: ")
+ password, err = term.ReadPassword(int(os.Stdin.Fd()))
+ if err != nil {
+ fmt.Println("Error reading password!")
+ return 1
+ }
+ fmt.Print(strings.Repeat("*", len(password)), " | Confirm: ")
+ confirmp, err = term.ReadPassword(int(os.Stdin.Fd()))
+ if err != nil {
+ fmt.Println("Error reading password!")
+ return 1
+ }
+ fmt.Println(strings.Repeat("*", len(confirmp)))
+ if !bytes.Equal(password, confirmp) {
+ fmt.Println("Passwords don't match!")
+ return 1
+ }
+ } else {
+ fmt.Print("Password: ")
+ password, err = term.ReadPassword(int(os.Stdin.Fd()))
+ if err != nil {
+ fmt.Println("Error reading password!")
+ return 1
+ }
+ fmt.Println(strings.Repeat("*", len(password)))
+ }
+
+ return 0
+}
+
+var pin string
+var pout string
+var pzip string
+var file *os.File
+var writer *zip.Writer
+var files []string
+var interrupted bool
+
+func prepare() int {
+ if mode == "decrypt" {
+ pin = flag.Arg(0)
+ pout = strings.TrimSuffix(pin, ".pcv")
+ } else {
+ stat, err := os.Stat(flag.Arg(0))
+ if flag.NArg() == 1 && err == nil && !stat.IsDir() {
+ pin = flag.Arg(0)
+ pout = pin + ".pcv"
+ } else {
+ items := []string{}
+ for _, v := range flag.Args() {
+ if strings.Contains(v, "../") || strings.HasPrefix(v, "/") {
+ fmt.Println("Cannot encrypt outside of current directory.")
+ return 1
+ }
+ matches, err := filepath.Glob(v)
+ if err != nil {
+ fmt.Println("Invalid glob pattern:", v)
+ return 1
+ }
+ items = append(items, matches...)
+ }
+ for _, v := range items {
+ stat, err := os.Stat(v)
+ if err != nil {
+ fmt.Println("Cannot access input:", v)
+ return 1
+ }
+ if !stat.IsDir() {
+ files = append(files, v)
+ } else {
+ filepath.Walk(v, func(path string, _ os.FileInfo, _ error) error {
+ stat, err := os.Stat(path)
+ if err == nil && !stat.IsDir() {
+ files = append(files, path)
+ }
+ return nil
+ })
+ }
+ }
+ if len(files) == 0 {
+ fmt.Println("Nothing to encrypt!")
+ return 1
+ }
+ }
+ }
+
+ return 0
+}
+
+func compress() int {
+ if files == nil {
+ return 0
+ }
+
+ dir, err := os.Getwd()
+ if err != nil {
+ fmt.Println("Cannot get current working directory!")
+ return 1
+ }
+ dir = filepath.ToSlash(dir)
+ file, err = os.CreateTemp("", "picocrypt-cli-v2-*.tmp")
+ if err != nil {
+ fmt.Println("Cannot create temporary file!")
+ return 1
+ }
+ pzip = file.Name()
+ writer = zip.NewWriter(file)
+
+ for i, path := range files {
+ stat, err := os.Stat(path)
+ if err != nil {
+ continue
+ }
+ header, err := zip.FileInfoHeader(stat)
+ if err != nil {
+ continue
+ }
+ abs, err := filepath.Abs(path)
+ if err != nil {
+ continue
+ }
+ abs = filepath.ToSlash(abs)
+ header.Name = strings.TrimPrefix(abs, dir)
+ header.Name = strings.TrimPrefix(header.Name, "/")
+ header.Method = zip.Deflate
+ entry, err := writer.CreateHeader(header)
+ if err != nil {
+ continue
+ }
+ fin, err := os.Open(path)
+ if err != nil {
+ writer.Close()
+ file.Close()
+ fmt.Println("Read access to input denied:", path)
+ return 1
+ }
+ bar := progressbar.NewOptions(
+ int(stat.Size()),
+ progressbar.OptionClearOnFinish(),
+ progressbar.OptionFullWidth(),
+ progressbar.OptionShowBytes(true),
+ progressbar.OptionUseIECUnits(true),
+ progressbar.OptionSetDescription(
+ fmt.Sprintf("Compressing [%d/%d]:", i+1, len(files)),
+ ),
+ )
+ _, err = io.Copy(io.MultiWriter(entry, bar), fin)
+ fin.Close()
+ if err != nil {
+ if interrupted {
+ time.Sleep(1 * time.Second)
+ } else {
+ writer.Close()
+ file.Close()
+ fmt.Println("Insufficient disk space!")
+ return 1
+ }
+ }
+ }
+ writer.Close()
+ file.Close()
+ pin = file.Name()
+ pout = "encrypted-" + strconv.Itoa(int(time.Now().Unix())) + ".zip.pcv"
+
+ return 0
+}
+
+var fin *os.File
+var fout *os.File
+var padded bool
+var salt []byte
+var hkdfSalt []byte
+var serpentIV []byte
+var nonce []byte
+var keyHash []byte
+var keyHashRef []byte
+var authTag []byte
+var key []byte
+var mac hash.Hash
+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)
+var rs128, _ = infectious.NewFEC(128, 136)
+
+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, fast bool) ([]byte, error) {
+ if rs.Total() == 136 && fast {
+ return data[:128], nil
+ }
+ tmp := make([]infectious.Share, rs.Total())
+ for i := 0; i < rs.Total(); i++ {
+ tmp[i].Number = i
+ tmp[i].Data = append(tmp[i].Data, data[i])
+ }
+ res, err := rs.Decode(nil, tmp)
+ if err != nil {
+ if rs.Total() == 136 {
+ return data[:128], err
+ }
+ return data[:rs.Total()/3], err
+ }
+ return res, nil
+}
+
+func pad(data []byte) []byte {
+ padLen := 128 - len(data)%128
+ padding := bytes.Repeat([]byte{byte(padLen)}, padLen)
+ return append(data, padding...)
+}
+
+func unpad(data []byte) []byte {
+ padLen := int(data[127])
+ return data[:128-padLen]
+}
+
+func work() int {
+ fin, err = os.Open(pin)
+ if err != nil {
+ fmt.Println("Error accessing input file:", pin)
+ return 1
+ }
+ _, err = os.Stat(pout)
+ if err == nil {
+ fmt.Println("Output file already exists!")
+ return 1
+ }
+ fout, err = os.Create(pout)
+ if err != nil {
+ fmt.Println("Error creating output file:", pout)
+ return 1
+ }
+ stat, err := os.Stat(pin)
+ if err != nil {
+ fmt.Println("Error accessing input file:", pin)
+ return 1
+ }
+ total := stat.Size()
+ if mode == "decrypt" {
+ total -= 789
+ }
+
+ if mode == "encrypt" {
+ errs := make([]error, 10)
+ salt = make([]byte, 16)
+ hkdfSalt = make([]byte, 32)
+ serpentIV = make([]byte, 16)
+ nonce = make([]byte, 24)
+ _, errs[0] = fout.Write(rsEncode(rs5, []byte("v1.34")))
+ _, errs[1] = fout.Write(rsEncode(rs5, []byte("00000")))
+ flags := make([]byte, 5)
+ if *p {
+ flags[0] = 1
+ }
+ if *r {
+ flags[3] = 1
+ }
+ if total%int64(MiB) >= int64(MiB)-128 {
+ flags[4] = 1
+ }
+ _, errs[2] = fout.Write(rsEncode(rs5, flags))
+ rand.Read(salt)
+ rand.Read(hkdfSalt)
+ rand.Read(serpentIV)
+ rand.Read(nonce)
+ _, errs[3] = fout.Write(rsEncode(rs16, salt))
+ _, errs[4] = fout.Write(rsEncode(rs32, hkdfSalt))
+ _, errs[5] = fout.Write(rsEncode(rs16, serpentIV))
+ _, errs[6] = fout.Write(rsEncode(rs24, nonce))
+ _, errs[7] = fout.Write(make([]byte, 192))
+ _, errs[8] = fout.Write(make([]byte, 96))
+ _, errs[9] = fout.Write(make([]byte, 192))
+ for _, err := range errs {
+ if err != nil {
+ fin.Close()
+ fout.Close()
+ fmt.Println("Insufficient disk space!")
+ return 1
+ }
+ }
+ } else {
+ errs := make([]error, 9)
+ version := make([]byte, 15)
+ fin.Read(version)
+ _, errs[0] = rsDecode(rs5, version, !(*f))
+ tmp := make([]byte, 15)
+ fin.Read(tmp)
+ tmp, errs[1] = rsDecode(rs5, tmp, !(*f))
+ comments, _ := strconv.Atoi(string(tmp))
+ fin.Read(make([]byte, comments*3))
+ total -= int64(comments) * 3
+ flags := make([]byte, 15)
+ fin.Read(flags)
+ flags, errs[2] = rsDecode(rs5, flags, !(*f))
+ *p = flags[0] == 1
+ *r = flags[3] == 1
+ padded = flags[4] == 1
+ if flags[1] == 1 {
+ fin.Close()
+ fout.Close()
+ fmt.Println("Keyfiles are not supported!")
+ return 1
+ }
+ salt = make([]byte, 48)
+ fin.Read(salt)
+ salt, errs[3] = rsDecode(rs16, salt, !(*f))
+ hkdfSalt = make([]byte, 96)
+ fin.Read(hkdfSalt)
+ hkdfSalt, errs[4] = rsDecode(rs32, hkdfSalt, !(*f))
+ serpentIV = make([]byte, 48)
+ fin.Read(serpentIV)
+ serpentIV, errs[5] = rsDecode(rs16, serpentIV, !(*f))
+ nonce = make([]byte, 72)
+ fin.Read(nonce)
+ nonce, errs[6] = rsDecode(rs24, nonce, !(*f))
+ keyHashRef = make([]byte, 192)
+ fin.Read(keyHashRef)
+ keyHashRef, errs[7] = rsDecode(rs64, keyHashRef, !(*f))
+ fin.Read(make([]byte, 96))
+ authTag = make([]byte, 192)
+ fin.Read(authTag)
+ authTag, errs[8] = rsDecode(rs64, authTag, !(*f))
+ for _, err := range errs {
+ if err != nil {
+ fin.Close()
+ fout.Close()
+ fmt.Println("The volume header is irrecoverably damaged!")
+ return 1
+ }
+ }
+ }
+
+ if *p {
+ key = argon2.IDKey(password, salt, 8, 1<<20, 8, 32)
+ } else {
+ key = argon2.IDKey(password, salt, 4, 1<<20, 4, 32)
+ }
+ tmp := sha3.New512()
+ tmp.Write(key)
+ keyHash = tmp.Sum(nil)
+ if mode == "decrypt" {
+ if !bytes.Equal(keyHash, keyHashRef) {
+ fin.Close()
+ fout.Close()
+ fmt.Println("Incorrect password!")
+ return 1
+ }
+ }
+
+ done, counter := 0, 0
+ chacha, _ := chacha20.NewUnauthenticatedCipher(key, nonce)
+ subkey := make([]byte, 32)
+ hkdf := hkdf.New(sha3.New256, key, hkdfSalt, nil)
+ hkdf.Read(subkey)
+ if *p {
+ mac = hmac.New(sha3.New512, subkey)
+ } else {
+ mac, _ = blake2b.New512(subkey)
+ }
+ serpentKey := make([]byte, 32)
+ hkdf.Read(serpentKey)
+ s, _ := serpent.NewCipher(serpentKey)
+ serpent := cipher.NewCTR(s, serpentIV)
+
+ bar := progressbar.NewOptions(
+ int(total),
+ progressbar.OptionClearOnFinish(),
+ progressbar.OptionFullWidth(),
+ progressbar.OptionShowBytes(true),
+ progressbar.OptionUseIECUnits(true),
+ progressbar.OptionSetDescription(
+ (func() string {
+ if mode == "encrypt" {
+ return "Encrypting:"
+ }
+ return "Decrypting:"
+ })(),
+ ),
+ )
+ for {
+ var src []byte
+ if mode == "decrypt" && *r {
+ src = make([]byte, MiB/128*136)
+ } else {
+ src = make([]byte, MiB)
+ }
+ size, err := fin.Read(src)
+ if err != nil {
+ break
+ }
+ src = src[:size]
+ dst := make([]byte, len(src))
+ bar.Write(src)
+
+ if mode == "encrypt" {
+ if *p {
+ serpent.XORKeyStream(dst, src)
+ copy(src, dst)
+ }
+ chacha.XORKeyStream(dst, src)
+ mac.Write(dst)
+ if *r {
+ copy(src, dst)
+ dst = nil
+ if len(src) == MiB {
+ for i := 0; i < MiB; i += 128 {
+ dst = append(dst, rsEncode(rs128, src[i:i+128])...)
+ }
+ } else {
+ chunks := math.Floor(float64(len(src)) / 128)
+ for i := 0; float64(i) < chunks; i++ {
+ dst = append(dst, rsEncode(rs128, src[i*128:(i+1)*128])...)
+ }
+ dst = append(dst, rsEncode(rs128, pad(src[int(chunks*128):]))...)
+ }
+ }
+ } else {
+ if *r {
+ copy(dst, src)
+ src = nil
+ if len(dst) == MiB/128*136 {
+ for i := 0; i < MiB/128*136; i += 136 {
+ tmp, err := rsDecode(rs128, dst[i:i+136], !(*f))
+ if err != nil {
+ fin.Close()
+ fout.Close()
+ fmt.Println("\nThe input file is irrecoverably damaged.")
+ return 1
+ }
+ if i == MiB/128*136-136 && done+MiB/128*136 >= int(total) && padded {
+ tmp = unpad(tmp)
+ }
+ src = append(src, tmp...)
+ }
+ } else {
+ chunks := len(dst)/136 - 1
+ for i := 0; i < chunks; i++ {
+ tmp, err := rsDecode(rs128, dst[i*136:(i+1)*136], !(*f))
+ if err != nil {
+ fin.Close()
+ fout.Close()
+ fmt.Println("\nThe input file is irrecoverably damaged.")
+ return 1
+ }
+ src = append(src, tmp...)
+ }
+ tmp, err := rsDecode(rs128, dst[int(chunks)*136:], !(*f))
+ if err != nil {
+ fin.Close()
+ fout.Close()
+ fmt.Println("\nThe input file is irrecoverably damaged.")
+ return 1
+ }
+ src = append(src, unpad(tmp)...)
+ }
+ dst = make([]byte, len(src))
+ }
+ mac.Write(src)
+ chacha.XORKeyStream(dst, src)
+ if *p {
+ copy(src, dst)
+ serpent.XORKeyStream(dst, src)
+ }
+ }
+
+ _, err = fout.Write(dst)
+ if err != nil {
+ if interrupted {
+ time.Sleep(1 * time.Second)
+ } else {
+ fin.Close()
+ fout.Close()
+ fmt.Println("\nInsufficient disk space!")
+ return 1
+ }
+ }
+ if mode == "decrypt" && *r {
+ done += MiB / 128 * 136
+ } else {
+ done += MiB
+ }
+
+ if counter >= 60*GiB {
+ nonce = make([]byte, 24)
+ hkdf.Read(nonce)
+ chacha, _ = chacha20.NewUnauthenticatedCipher(key, nonce)
+ serpentIV = make([]byte, 16)
+ hkdf.Read(serpentIV)
+ serpent = cipher.NewCTR(s, serpentIV)
+ counter = 0
+ }
+ }
+
+ if mode == "encrypt" {
+ 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) {
+ fin.Close()
+ fout.Close()
+ if *k {
+ fmt.Println("\nThe modified output has been kept.")
+ return 0
+ } else {
+ fmt.Println("\nThe input volume is damaged or modified!")
+ if *r {
+ fmt.Println("Fortunately, this volume is encoded with Reed-Solomon.")
+ fmt.Println("Try again using the '-f' flag to repair the corruption.")
+ }
+ return 1
+ }
+ }
+ }
+
+ fin.Close()
+ fout.Close()
+ fmt.Println("Completed ->", fout.Name())
+ return 0
+}
+
+func main() {
+ if parse() == 1 {
+ os.Exit(1)
+ }
+ if auth() == 1 {
+ os.Exit(1)
+ }
+ if prepare() == 1 {
+ os.Exit(1)
+ }
+
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt)
+ go func() {
+ <-c
+ interrupted = true
+ if fin != nil {
+ fin.Close()
+ }
+ if fout != nil {
+ fmt.Print("\nSystem interrupt detected, cleaning up incomplete output: ")
+ fout.Close()
+ if err := os.Remove(fout.Name()); err == nil {
+ fmt.Print("Success.")
+ } else {
+ fmt.Print("Failure.")
+ }
+ }
+ if pzip != "" {
+ fmt.Print("\nSystem interrupt detected, cleaning up temporary files: ")
+ writer.Close()
+ file.Close()
+ if err := os.Remove(pzip); err == nil {
+ fmt.Print("Success.")
+ } else {
+ fmt.Print("Failure.")
+ }
+ }
+ fmt.Println()
+ os.Exit(1)
+ }()
+
+ if compress() == 1 {
+ os.Remove(pzip)
+ os.Exit(1)
+ } else {
+ defer os.Remove(pzip)
+ }
+ if work() == 1 {
+ if pzip != "" {
+ os.Remove(pzip)
+ }
+ if fout != nil {
+ os.Remove(fout.Name())
+ }
+ os.Exit(1)
+ }
+}