From bdca4f57c472aecb68cd52d164deb6015b26c08d Mon Sep 17 00:00:00 2001 From: Evan Su <48808396+HACKERALERT@users.noreply.github.com> Date: Wed, 22 Dec 2021 11:30:41 -0500 Subject: [PATCH] Picocrypt is stable, so deleting unstable folder --- src/unstable/Picocrypt.go | 2214 ------------------------------------- src/unstable/README.md | 1 - 2 files changed, 2215 deletions(-) delete mode 100644 src/unstable/Picocrypt.go delete mode 100644 src/unstable/README.md diff --git a/src/unstable/Picocrypt.go b/src/unstable/Picocrypt.go deleted file mode 100644 index 153c985..0000000 --- a/src/unstable/Picocrypt.go +++ /dev/null @@ -1,2214 +0,0 @@ -package main - -/* - -Picocrypt v1.21 -Copyright (c) Evan Su (https://evansu.cc) -Released under a GNU GPL v3 License -https://github.com/HACKERALERT/Picocrypt - -~ In cryptography we trust ~ - -*/ - -import ( - _ "embed" - - // Generic - "archive/zip" - "bytes" - "encoding/hex" - "encoding/json" - "fmt" - "hash" - "image" - "image/color" - "image/png" - "io" - "math" - "math/big" - "net/http" - "os" - "path/filepath" - "regexp" - "runtime/debug" - "strconv" - "strings" - "time" - - // Cryptography - "crypto/cipher" - "crypto/hmac" - "crypto/md5" - "crypto/rand" - "crypto/sha1" - "crypto/sha256" - "crypto/subtle" - - "github.com/HACKERALERT/serpent" - "golang.org/x/crypto/argon2" - "golang.org/x/crypto/blake2b" - "golang.org/x/crypto/blake2s" - "golang.org/x/crypto/chacha20" - "golang.org/x/crypto/hkdf" - "golang.org/x/crypto/sha3" - - // UI - "github.com/HACKERALERT/giu" - - // Reed-Solomon - "github.com/HACKERALERT/infectious" - - // Helpers - "github.com/HACKERALERT/clipboard" - "github.com/HACKERALERT/dialog" - "github.com/HACKERALERT/jibber_jabber" - "github.com/HACKERALERT/zxcvbn-go" -) - -//go:embed icon.png -var icon []byte - -//go:embed font.ttf -var font []byte - -//go:embed strings.json -var localeBytes []byte - -// Localization -type locale struct { - iso string - data []string -} - -var locales []locale -var selectedLocale = "en" -var allLocales = []string{ - "en", -} -var languages = []string{ - "English", -} -var languageSelected int32 - -// Generic variables -var version = "v1.21" -var window *giu.MasterWindow -var windowOptimized bool -var dpi float32 -var tab = 0 -var mode string -var working bool -var recombine bool - -// Three variables store the input files -var onlyFiles []string -var onlyFolders []string -var allFiles []string - -// Input file variables -var inputLabel = "Drop files and folders into this window." -var inputFile string - -// Password variables -var password string -var cPassword string -var passwordStrength int -var passwordState = giu.InputTextFlagsPassword -var passwordStateLabel = "Show" - -// Password generator variables -var showGenpass = false -var genpassCopy = true -var genpassLength int32 = 32 -var genpassUpper = true -var genpassLower = true -var genpassNums = true -var genpassSymbols = true - -// Keyfile variables -var keyfile bool -var keyfiles []string -var keyfileOrderMatters bool -var keyfilePrompt = "None selected." -var showKeyfile bool - -// Metadata variables -var metadata string -var metadataPrompt = "Metadata:" -var metadataDisabled bool - -// Advanced options -var fast bool -var paranoid bool -var reedsolo bool -var deleteWhenDone bool -var split bool -var splitSize string -var splitUnits = []string{ - "KiB", - "MiB", - "GiB", -} -var splitSelected int32 = 1 -var compress bool -var keep bool -var kept bool - -// Output file variables -var outputFile string - -// Status variables -var mainStatus = "Ready." -var mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff} -var popupStatus string - -// Progress variables -var progress float32 -var progressInfo string -var showProgress bool - -// Confirm overwrite variables -var showConfirmation bool - -// Reed-Solomon encoders -var rs1, _ = infectious.NewFEC(1, 3) // 1 data shard, 3 total -> 2 parity shards -var rs5, _ = infectious.NewFEC(5, 15) -var rs6, _ = infectious.NewFEC(6, 18) -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) - -// File checksum generator variables -var csMd5 string -var csSha1 string -var csSha256 string -var csSha3 string -var csBlake2b string -var csBlake2s string -var csValidate string -var md5Color = color.RGBA{0x00, 0x00, 0x00, 0x00} -var sha1Color = color.RGBA{0x00, 0x00, 0x00, 0x00} -var sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00} -var sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00} -var blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00} -var blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00} -var csProgress float32 = 0 -var md5Selected = true -var sha1Selected = true -var sha256Selected = true -var sha3Selected = false -var blake2bSelected = false -var blake2sSelected = false - -func draw() { - giu.SingleWindow().Flags(giu.WindowFlagsNoDecoration|giu.WindowFlagsNoNavFocus|giu.WindowFlagsNoMove| - giu.WindowFlagsNoScrollWithMouse).Layout( - giu.Custom(func() { - pos := giu.GetCursorPos() - w, _ := giu.CalcTextSize(languages[languageSelected]) - giu.Row( - giu.Dummy(-w/dpi-34, 0), - giu.Combo("##language", languages[languageSelected], languages, &languageSelected).OnChange(func() { - selectedLocale = allLocales[languageSelected] - }).Size(w/dpi+26), - ).Build() - giu.SetCursorPos(pos) - - giu.TabBar().TabItems( - giu.TabItem(s("Encryption")).Layout( - giu.Custom(func() { - if giu.IsItemActive() { - tab = 0 - } - }), - - giu.Custom(func() { - if showGenpass { - giu.PopupModal(s("Generate password:")). - Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize).Layout( - giu.Row( - giu.Label(s("Length:")), - giu.SliderInt(&genpassLength, 4, 64).Size(giu.Auto), - ), - giu.Checkbox(s("Uppercase"), &genpassUpper), - giu.Checkbox(s("Lowercase"), &genpassLower), - giu.Checkbox(s("Numbers"), &genpassNums), - giu.Checkbox(s("Symbols"), &genpassSymbols), - giu.Checkbox(s("Copy to clipboard"), &genpassCopy), - giu.Row( - giu.Button(s("Cancel")).Size(100, 0).OnClick(func() { - giu.CloseCurrentPopup() - showGenpass = false - }), - giu.Button(s("Generate")).Size(100, 0).OnClick(func() { - tmp := genPassword() - password = tmp - cPassword = tmp - passwordStrength = zxcvbn.PasswordStrength(password, nil).Score - giu.CloseCurrentPopup() - showGenpass = false - giu.Update() - }), - ), - ).Build() - giu.OpenPopup(s("Generate password:")) - giu.Update() - } - }), - - giu.Custom(func() { - if showKeyfile { - giu.PopupModal(s("Manage keyfiles:")). - Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize|giu.WindowFlagsAlwaysAutoResize).Layout( - giu.Label(s("Drag and drop your keyfiles here.")), - giu.Custom(func() { - if mode != "decrypt" { - giu.Checkbox(s("Require correct keyfile order"), &keyfileOrderMatters).Build() - giu.Tooltip(s("If checked, you will need to drop keyfiles in the correct order.")).Build() - } else if keyfileOrderMatters { - giu.Label(s("The correct order of keyfiles is required.")).Build() - } - }), - - giu.Custom(func() { - for _, i := range keyfiles { - giu.Row( - giu.SmallButton("×").OnClick(func() { - var tmp []string - for _, j := range keyfiles { - if j != i { - tmp = append(tmp, j) - } - } - keyfiles = tmp - if len(keyfiles) == 0 { - keyfilePrompt = s("None selected.") - } else if len(keyfiles) == 1 { - keyfilePrompt = s("Using 1 keyfile.") - } else { - keyfilePrompt = fmt.Sprintf(s("Using %d keyfiles."), len(keyfiles)) - } - }), - giu.Label(filepath.Base(i)), - ).Build() - - } - }), - giu.Row( - giu.Button(s("Clear")).Size(150, 0).OnClick(func() { - keyfiles = nil - keyfilePrompt = s("None selected.") - }), - giu.Tooltip(s("Remove all keyfiles.")), - giu.Button(s("Done")).Size(150, 0).OnClick(func() { - giu.CloseCurrentPopup() - showKeyfile = false - }), - ), - ).Build() - giu.OpenPopup(s("Manage keyfiles:")) - giu.Update() - } - }), - - giu.Custom(func() { - if showConfirmation { - giu.PopupModal(s("Warning:")). - Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize).Layout( - giu.Label(s("Output already exists. Overwrite?")), - giu.Row( - giu.Button(s("No")).Size(100, 0).OnClick(func() { - giu.CloseCurrentPopup() - showConfirmation = false - }), - giu.Button(s("Yes")).Size(100, 0).OnClick(func() { - giu.CloseCurrentPopup() - showConfirmation = false - showProgress = true - giu.Update() - go func() { - work() - working = false - showProgress = false - debug.FreeOSMemory() - giu.Update() - }() - }), - ), - ).Build() - giu.OpenPopup(s("Warning:")) - giu.Update() - } - }), - - giu.Custom(func() { - if showProgress { - giu.PopupModal(" "). - Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize).Layout( - giu.Custom(func() { - if !working { - giu.CloseCurrentPopup() - } - }), - giu.Row( - giu.ProgressBar(progress).Size(280, 0).Overlay(progressInfo), - giu.Button(s("Cancel")).Size(58, 0).OnClick(func() { - working = false - }), - ), - giu.Label(popupStatus), - ).Build() - giu.OpenPopup(" ") - giu.Update() - } - }), - - giu.Row( - giu.Label(inputLabel), - giu.Custom(func() { - bw, _ := giu.CalcTextSize(s("Clear")) - p, _ := giu.GetWindowPadding() - bw += p * 2 - giu.Dummy(float32(float64(-(bw+p)/dpi)), 0).Build() - giu.SameLine() - giu.Style().SetDisabled(len(allFiles) == 0 && len(onlyFiles) == 0).To( - giu.Button(s("Clear")).Size(bw/dpi, 0).OnClick(resetUI), - giu.Tooltip(s("Clear all input files and reset UI state.")), - ).Build() - }), - ), - - giu.Separator(), - - giu.Style().SetDisabled(len(allFiles) == 0 && len(onlyFiles) == 0).To( - giu.Row( - giu.Label(s("Password:")), - giu.Dummy(-124, 0), - giu.Style().SetDisabled(mode == "decrypt" && !keyfile).To( - giu.Label(s("Keyfiles:")), - ), - ), - giu.Row( - giu.Button(s(passwordStateLabel)).Size(54, 0).OnClick(func() { - if passwordState == giu.InputTextFlagsPassword { - passwordState = giu.InputTextFlagsNone - passwordStateLabel = "Hide" - } else { - passwordState = giu.InputTextFlagsPassword - passwordStateLabel = "Show" - } - }), - - giu.Button(s("Clear")).Size(54, 0).OnClick(func() { - password = "" - cPassword = "" - }), - - giu.Button(s("Copy")).Size(54, 0).OnClick(func() { - clipboard.WriteAll(password) - }), - - giu.Button(s("Paste")).Size(54, 0).OnClick(func() { - tmp, _ := clipboard.ReadAll() - password = tmp - if mode != "decrypt" { - cPassword = tmp - } - passwordStrength = zxcvbn.PasswordStrength(password, nil).Score - giu.Update() - }), - - giu.Style().SetDisabled(mode == "decrypt").To( - giu.Button(s("Create")).Size(54, 0).OnClick(func() { - showGenpass = true - }), - ), - - giu.Style().SetDisabled(mode == "decrypt" && !keyfile).To( - giu.Row( - giu.Button(s("Edit")).Size(54, 0).OnClick(func() { - showKeyfile = true - }), - giu.Style().SetDisabled(mode == "decrypt").To( - giu.Button(s("Create")).Size(54, 0).OnClick(func() { - file, _ := dialog.File().Title(s("Save keyfile as:")).Save() - if file == "" { - return - } - fout, _ := os.Create(file) - data := make([]byte, 1048576) - rand.Read(data) - fout.Write(data) - fout.Close() - }), - ), - ), - ), - ), - giu.Row( - giu.InputText(&password).Flags(passwordState).Size(302/dpi).OnChange(func() { - passwordStrength = zxcvbn.PasswordStrength(password, nil).Score - }), - giu.Custom(func() { - c := giu.GetCanvas() - p := giu.GetCursorScreenPos() - - var col color.RGBA - switch passwordStrength { - case 0: - col = color.RGBA{0xc8, 0x4c, 0x4b, 0xff} - case 1: - col = color.RGBA{0xa9, 0x6b, 0x4b, 0xff} - case 2: - col = color.RGBA{0x8a, 0x8a, 0x4b, 0xff} - case 3: - col = color.RGBA{0x6b, 0xa9, 0x4b, 0xff} - case 4: - col = color.RGBA{0x4c, 0xc8, 0x4b, 0xff} - } - if password == "" || mode == "decrypt" { - col = color.RGBA{0xff, 0xff, 0xff, 0x00} - } - - path := p.Add(image.Pt( - int(math.Round(float64(-20*dpi))), - int(math.Round(float64(12*dpi))), - )) - c.PathArcTo(path, 6*dpi, -math.Pi/2, float32(passwordStrength+1)/5*2*math.Pi-math.Pi/2, -1) - c.PathStroke(col, false, 2) - }), - giu.Style().SetDisabled(true).To( - giu.InputText(&keyfilePrompt).Size(giu.Auto), - ), - ), - ), - - giu.Style().SetDisabled(password == "").To( - giu.Row( - giu.Style().SetDisabled(mode == "decrypt").To( - giu.Label(s("Confirm password:")), - ), - giu.Dummy(-124, 0), - giu.Style().SetDisabled(true).To( - giu.Label(s("Custom Argon2:")), - ), - ), - ), - giu.Style().SetDisabled(password == "").To( - giu.Row( - giu.Style().SetDisabled(mode == "decrypt").To( - giu.Row( - giu.InputText(&cPassword).Flags(passwordState).Size(302/dpi), - giu.Custom(func() { - c := giu.GetCanvas() - p := giu.GetCursorScreenPos() - col := color.RGBA{0x4c, 0xc8, 0x4b, 0xff} - - if cPassword != password { - col = color.RGBA{0xc8, 0x4c, 0x4b, 0xff} - } - if password == "" || cPassword == "" || mode == "decrypt" { - col = color.RGBA{0xff, 0xff, 0xff, 0x00} - } - - path := p.Add(image.Pt( - int(math.Round(float64(-20*dpi))), - int(math.Round(float64(12*dpi))), - )) - c.PathArcTo(path, 6*dpi, 0, 2*math.Pi, -1) - c.PathStroke(col, false, 2) - }), - ), - ), - giu.Style().SetDisabled(true).To( - giu.Button(s("W.I.P")).Size(giu.Auto, 0), - ), - ), - ), - - giu.Dummy(0, 3), - giu.Separator(), - giu.Dummy(0, 0), - - giu.Style().SetDisabled(password == "" || (password != cPassword && mode == "encrypt")).To( - giu.Label(s(metadataPrompt)), - giu.Style().SetDisabled(metadataDisabled).To( - giu.InputText(&metadata).Size(giu.Auto), - ), - - giu.Label(s("Advanced:")), - giu.Custom(func() { - if mode != "decrypt" { - giu.Row( - giu.Checkbox(s("Use fast mode"), &fast), - giu.Dummy(-221, 0), - giu.Checkbox(s("Encode with Reed-Solomon"), &reedsolo), - ).Build() - giu.Row( - giu.Checkbox(s("Use paranoid mode"), ¶noid), - giu.Dummy(-221, 0), - giu.Checkbox(s("Delete files when complete"), &deleteWhenDone), - ).Build() - giu.Row( - giu.Style().SetDisabled(!(len(allFiles) > 1 || len(onlyFolders) > 0)).To( - giu.Checkbox(s("Compress files"), &compress), - ), - giu.Dummy(-221, 0), - giu.Checkbox(s("Split every"), &split), - giu.InputText(&splitSize).Size(55/dpi).Flags(giu.InputTextFlagsCharsHexadecimal).OnChange(func() { - split = splitSize != "" - }), - giu.Combo("##splitter", splitUnits[splitSelected], splitUnits, &splitSelected).Size(52), - ).Build() - } else { - giu.Checkbox(s("Keep decrypted output even if it's corrupted or modified"), &keep).Build() - giu.Checkbox(s("Delete the encrypted files after a successful decryption"), &deleteWhenDone).Build() - } - }), - - giu.Label(s("Save output as:")), - giu.Custom(func() { - w, _ := giu.GetAvailableRegion() - bw, _ := giu.CalcTextSize(s("Change")) - p, _ := giu.GetWindowPadding() - bw += p * 2 - dw := w - bw - p - giu.Style().SetDisabled(true).To( - giu.InputText(&outputFile).Size(dw / dpi / dpi).Flags(giu.InputTextFlagsReadOnly), - ).Build() - giu.SameLine() - giu.Button(s("Change")).Size(bw/dpi, 0).OnClick(func() { - file, _ := dialog.File().Title(s("Save as:")).Save() - if file == "" { - return - } - - if mode == "encrypt" { - if len(allFiles) > 1 || len(onlyFolders) > 0 { - file = strings.TrimSuffix(file, ".zip.pcv") - file = strings.TrimSuffix(file, ".pcv") - if !strings.HasSuffix(file, ".zip.pcv") { - file += ".zip.pcv" - } - } else { - file = strings.TrimSuffix(file, ".pcv") - ind := strings.Index(inputFile, ".") - file += inputFile[ind:] - if !strings.HasSuffix(file, ".pcv") { - file += ".pcv" - } - } - } else { - ind := strings.Index(file, ".") - if ind != -1 { - file = file[:ind] - } - if strings.HasSuffix(inputFile, ".zip.pcv") { - file += ".zip" - } else { - tmp := strings.TrimSuffix(filepath.Base(inputFile), ".pcv") - tmp = tmp[strings.Index(tmp, "."):] - file += tmp - } - } - - outputFile = file - }).Build() - giu.Tooltip(s("Save the output with a custom path and name.")).Build() - }), - - giu.Dummy(0, 2), - giu.Separator(), - giu.Dummy(0, 3), - - giu.Button(s("Start")).Size(giu.Auto, 34).OnClick(func() { - if keyfile && keyfiles == nil { - mainStatus = "Please select your keyfiles." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - return - } - _, err := os.Stat(outputFile) - if err == nil { - showConfirmation = true - giu.Update() - } else { - showProgress = true - giu.Update() - go func() { - work() - working = false - showProgress = false - debug.FreeOSMemory() - giu.Update() - }() - } - }), - giu.Style().SetColor(giu.StyleColorText, mainStatusColor).To( - giu.Label(s(mainStatus)), - ), - ), - ), - giu.TabItem(s("Checksum")).Layout( - giu.Custom(func() { - if giu.IsItemActive() { - tab = 1 - } - }), - giu.Label(s("Toggle the hashes you would like to generate and drop a file here.")), - - // MD5 - giu.Custom(func() { - giu.Checkbox("MD5:", &md5Selected).OnChange(func() { - csMd5 = "" - }).Build() - giu.SameLine() - w, _ := giu.GetAvailableRegion() - bw, _ := giu.CalcTextSize(s("Copy")) - padding, _ := giu.GetWindowPadding() - bw += 2 * padding - size := w - bw - padding - giu.Dummy(size/dpi, 0).Build() - giu.SameLine() - giu.Button(s("Copy")+"##md5").Size(bw/dpi, 0).OnClick(func() { - clipboard.WriteAll(csMd5) - }).Build() - }), - giu.Style().SetColor(giu.StyleColorBorder, md5Color).To( - giu.Style().SetDisabled(true).To( - giu.InputText(&csMd5).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly), - ), - ), - - // SHA1 - giu.Custom(func() { - giu.Checkbox("SHA1:", &sha1Selected).OnChange(func() { - csSha1 = "" - }).Build() - giu.SameLine() - w, _ := giu.GetAvailableRegion() - bw, _ := giu.CalcTextSize(s("Copy")) - padding, _ := giu.GetWindowPadding() - bw += 2 * padding - size := w - bw - padding - giu.Dummy(size/dpi, 0).Build() - giu.SameLine() - giu.Button(s("Copy")+"##sha1").Size(bw/dpi, 0).OnClick(func() { - clipboard.WriteAll(csSha1) - }).Build() - }), - giu.Style().SetColor(giu.StyleColorBorder, sha1Color).To( - giu.Style().SetDisabled(true).To( - giu.InputText(&csSha1).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly), - ), - ), - - // SHA256 - giu.Custom(func() { - giu.Checkbox("SHA256:", &sha256Selected).OnChange(func() { - csSha256 = "" - }).Build() - giu.SameLine() - w, _ := giu.GetAvailableRegion() - bw, _ := giu.CalcTextSize(s("Copy")) - padding, _ := giu.GetWindowPadding() - bw += 2 * padding - size := w - bw - padding - giu.Dummy(size/dpi, 0).Build() - giu.SameLine() - giu.Button(s("Copy")+"##sha256").Size(bw/dpi, 0).OnClick(func() { - clipboard.WriteAll(csSha256) - }).Build() - }), - giu.Style().SetColor(giu.StyleColorBorder, sha256Color).To( - giu.Style().SetDisabled(true).To( - giu.InputText(&csSha256).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly), - ), - ), - - // SHA3-256 - giu.Custom(func() { - giu.Checkbox("SHA3:", &sha3Selected).OnChange(func() { - csSha3 = "" - }).Build() - giu.SameLine() - w, _ := giu.GetAvailableRegion() - bw, _ := giu.CalcTextSize(s("Copy")) - padding, _ := giu.GetWindowPadding() - bw += 2 * padding - size := w - bw - padding - giu.Dummy(size/dpi, 0).Build() - giu.SameLine() - giu.Button(s("Copy")+"##sha3").Size(bw/dpi, 0).OnClick(func() { - clipboard.WriteAll(csSha3) - }).Build() - }), - giu.Style().SetColor(giu.StyleColorBorder, sha3Color).To( - giu.Style().SetDisabled(true).To( - giu.InputText(&csSha3).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly), - ), - ), - - // BLAKE2b - giu.Custom(func() { - giu.Checkbox("BLAKE2b:", &blake2bSelected).OnChange(func() { - csBlake2b = "" - }).Build() - giu.SameLine() - w, _ := giu.GetAvailableRegion() - bw, _ := giu.CalcTextSize(s("Copy")) - padding, _ := giu.GetWindowPadding() - bw += 2 * padding - size := w - bw - padding - giu.Dummy(size/dpi, 0).Build() - giu.SameLine() - giu.Button(s("Copy")+"##blake2b").Size(bw/dpi, 0).OnClick(func() { - clipboard.WriteAll(csBlake2b) - }).Build() - }), - giu.Style().SetColor(giu.StyleColorBorder, blake2bColor).To( - giu.Style().SetDisabled(true).To( - giu.InputText(&csBlake2b).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly), - ), - ), - - // BLAKE2s - giu.Custom(func() { - giu.Checkbox("BLAKE2s:", &blake2sSelected).OnChange(func() { - csBlake2s = "" - }).Build() - giu.SameLine() - w, _ := giu.GetAvailableRegion() - bw, _ := giu.CalcTextSize(s("Copy")) - padding, _ := giu.GetWindowPadding() - bw += 2 * padding - size := w - bw - padding - giu.Dummy(size/dpi, 0).Build() - giu.SameLine() - giu.Button(s("Copy")+"##blake2s").Size(bw/dpi, 0).OnClick(func() { - clipboard.WriteAll(csBlake2s) - }).Build() - }), - giu.Style().SetColor(giu.StyleColorBorder, blake2sColor).To( - giu.Style().SetDisabled(true).To( - giu.InputText(&csBlake2s).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly), - ), - ), - - // Input entry for validating a checksum - giu.Row( - giu.Label(s("Validate a checksum:")), - giu.Custom(func() { - bw, _ := giu.CalcTextSize(s("Paste")) - padding, _ := giu.GetWindowPadding() - bw += 2 * padding - giu.Button(s("Paste")).Size(bw/dpi, 0).OnClick(func() { - tmp, _ := clipboard.ReadAll() - csValidate = tmp - md5Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha1Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00} - blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00} - if csValidate == "" { - return - } - csValidate = strings.ToLower(csValidate) - if csValidate == csMd5 { - md5Color = color.RGBA{0x00, 0xff, 0x00, 0xff} - } else if csValidate == csSha1 { - sha1Color = color.RGBA{0x00, 0xff, 0x00, 0xff} - } else if csValidate == csSha256 { - sha256Color = color.RGBA{0x00, 0xff, 0x00, 0xff} - } else if csValidate == csSha3 { - sha3Color = color.RGBA{0x00, 0xff, 0x00, 0xff} - } else if csValidate == csBlake2b { - blake2bColor = color.RGBA{0x00, 0xff, 0x00, 0xff} - } else if csValidate == csBlake2s { - blake2sColor = color.RGBA{0x00, 0xff, 0x00, 0xff} - } - giu.Update() - }).Build() - }), - giu.Custom(func() { - bw, _ := giu.CalcTextSize(s("Paste")) - padding, _ := giu.GetWindowPadding() - bw += 2 * padding - giu.Button(s("Clear")).Size(bw/dpi, 0).OnClick(func() { - csValidate = "" - md5Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha1Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00} - blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00} - }).Build() - }), - ), - giu.Style().SetDisabled(true).To( - giu.InputText(&csValidate).Size(giu.Auto), - ), - - // Progress bar - giu.Label(s("Progress:")), - giu.ProgressBar(csProgress).Size(giu.Auto, 0), - ), - giu.TabItem(s("About")).Layout( - giu.Custom(func() { - if giu.IsItemActive() { - tab = 2 - } - }), - giu.Label(fmt.Sprintf(s("Picocrypt %s, created by Evan Su (https://evansu.cc/)."), version)), - giu.Label(s("Released under a GNU GPL v3 License.")), - giu.Label(s("A warm thank you to all the people listed below.")), - giu.Label(s("Patrons:")), - giu.Label(" - Frederick Doe"), - giu.Label(s("Donators:")), - giu.Label(" - jp26"), - giu.Label(" - W.Graham"), - giu.Label(" - N. Chin"), - giu.Label(" - Manjot"), - giu.Label(" - Phil P."), - giu.Label(" - E. Zahard"), - giu.Label(s("Translators:")), - giu.Label("umitseyhan75, digitalblossom, zeeaall, francirc, kurpau"), - giu.Label(s("Other:")), - giu.Label("Fuderal, u/greenreddits, u/Tall_Escape, u/NSABackdoors"), - ), - ).Build() - }), - giu.Custom(func() { - if !windowOptimized || windowOptimized { - windowOptimized = true - var pad int - if tab == 1 { - pad = 6 - } - if tab == 2 { - pad = 1 - } - if tab == 3 { - pad = 2 - } - window.SetSize(int(442*dpi), giu.GetCursorPos().Y+1+pad) - } - }), - ) -} - -func onDrop(names []string) { - if tab == 1 { - go generateChecksums(names[0]) - return - } - if tab == 2 { - return - } - - if showKeyfile { - keyfiles = append(keyfiles, names...) - var tmp []string - for _, i := range keyfiles { - duplicate := false - for _, j := range tmp { - if i == j { - duplicate = true - } - } - stat, _ := os.Stat(i) - if !duplicate && !stat.IsDir() { - tmp = append(tmp, i) - } - } - keyfiles = tmp - if len(keyfiles) == 1 { - keyfilePrompt = s("Using 1 keyfile.") - } else { - keyfilePrompt = fmt.Sprintf(s("Using %d keyfiles."), len(keyfiles)) - } - return - } - - // Clear variables - recombine = false - onlyFiles = nil - onlyFolders = nil - allFiles = nil - files, folders := 0, 0 - resetUI() - - if len(names) == 1 { - stat, _ := os.Stat(names[0]) - if stat.IsDir() { - // Update variables - mode = "encrypt" - folders++ - inputLabel = s("1 folder selected.") - - // Add the folder - onlyFolders = append(onlyFolders, names[0]) - - // Set the input and output paths - inputFile = filepath.Join(filepath.Dir(names[0]), s("Encrypted")) + ".zip" - outputFile = filepath.Join(filepath.Dir(names[0]), s("Encrypted")) + ".zip.pcv" - } else { - files++ - name := filepath.Base(names[0]) - nums := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} - endsNum := false - for _, i := range nums { - if strings.HasSuffix(names[0], i) { - endsNum = true - } - } - isSplit := strings.Contains(names[0], ".pcv.") && endsNum - - // Decide if encrypting or decrypting - if strings.HasSuffix(names[0], ".pcv") || isSplit { - //var err error - mode = "decrypt" - inputLabel = name + s(" (will decrypt)") - metadataPrompt = s("Metadata (read-only):") - metadataDisabled = true - - if isSplit { - inputLabel = name + s(" (will recombine and decrypt)") - ind := strings.Index(names[0], ".pcv") - names[0] = names[0][:ind+4] - inputFile = names[0] - outputFile = names[0][:ind] - recombine = true - } else { - outputFile = names[0][:len(names[0])-4] - } - - // Open input file in read-only mode - var fin *os.File - if isSplit { - fin, _ = os.Open(names[0] + ".0") - } else { - fin, _ = os.Open(names[0]) - } - - // Use regex to test if input is a valid Picocrypt volume - tmp := make([]byte, 30) - fin.Read(tmp) - if string(tmp[:5]) == "v1.13" { - resetUI() - mainStatus = "Please use Picocrypt v1.13 to decrypt this file." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - fin.Close() - return - } - if valid, _ := regexp.Match(`^v\d\.\d{2}.{10}0?\d+`, tmp); !valid && !isSplit { - resetUI() - mainStatus = "This doesn't seem to be a Picocrypt volume." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - fin.Close() - return - } - fin.Seek(0, 0) - - // Read metadata and insert into box - var err error - tmp = make([]byte, 15) - fin.Read(tmp) - tmp, _ = rsDecode(rs5, tmp) - if string(tmp) == "v1.14" || string(tmp) == "v1.15" || string(tmp) == "v1.16" { - resetUI() - mainStatus = "Please use Picocrypt v1.16 to decrypt this file." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - fin.Close() - return - } - tmp = make([]byte, 15) - fin.Read(tmp) - tmp, err = rsDecode(rs5, tmp) - - if err == nil { - metadataLength, _ := strconv.Atoi(string(tmp)) - tmp = make([]byte, metadataLength*3) - fin.Read(tmp) - metadata = "" - - for i := 0; i < metadataLength*3; i += 3 { - t, err := rsDecode(rs1, tmp[i:i+3]) - if err != nil { - metadata = s("Metadata is corrupted.") - break - } - metadata += string(t) - } - } else { - metadata = s("Metadata is corrupted.") - } - - flags := make([]byte, 18) - fin.Read(flags) - fin.Close() - flags, err = rsDecode(rs6, flags) - if err != nil { - mainStatus = "Input file is corrupt and cannot be decrypted." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - return - } - - if flags[2] == 1 { - keyfile = true - keyfilePrompt = s("Keyfiles required.") - } else { - keyfilePrompt = s("Not applicable.") - } - if flags[5] == 1 { - keyfileOrderMatters = true - } - } else { - mode = "encrypt" - inputLabel = name + s(" (will encrypt)") - inputFile = names[0] - outputFile = names[0] + ".pcv" - } - - // Add the file - onlyFiles = append(onlyFiles, names[0]) - inputFile = names[0] - } - } else { - mode = "encrypt" - - // There are multiple dropped items, check each one - for _, name := range names { - stat, _ := os.Stat(name) - - // Check if item is a file or a directory - if stat.IsDir() { - folders++ - onlyFolders = append(onlyFolders, name) - } else { - files++ - onlyFiles = append(onlyFiles, name) - allFiles = append(allFiles, name) - } - } - - if folders == 0 { - inputLabel = fmt.Sprintf(s("%d files selected."), files) - } else if files == 0 { - inputLabel = fmt.Sprintf(s("%d folders selected."), files) - } else { - if files == 1 && folders > 1 { - inputLabel = fmt.Sprintf(s("1 file and %d folders selected."), folders) - } else if folders == 1 && files > 1 { - inputLabel = fmt.Sprintf(s("%d files and 1 folder selected."), files) - } else if folders == 1 && files == 1 { - inputLabel = s("1 file and 1 folder selected.") - } else { - inputLabel = fmt.Sprintf(s("%d files and %d folders selected."), files, folders) - } - } - - // Set the input and output paths - inputFile = filepath.Join(filepath.Dir(names[0]), s("Encrypted")) + ".zip" - outputFile = filepath.Join(filepath.Dir(names[0]), s("Encrypted")) + ".zip.pcv" - } - // Recursively add all files to 'allFiles' - if folders > 0 { - for _, name := range onlyFolders { - filepath.Walk(name, func(path string, _ os.FileInfo, _ error) error { - stat, _ := os.Stat(path) - if !stat.IsDir() { - allFiles = append(allFiles, path) - } - return nil - }) - } - } -} - -func work() { - popupStatus = s("Starting...") - mainStatus = "Working..." - mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff} - working = true - padded := false - - var salt []byte - var hkdfSalt []byte - var serpentSalt []byte - var nonce []byte - var keyHash []byte - var _keyHash []byte - var keyfileKey []byte - var keyfileHash []byte = make([]byte, 32) - var _keyfileHash []byte - var dataMac []byte - - if mode == "encrypt" { - if compress { - popupStatus = s("Compressing files...") - } else { - popupStatus = s("Combining files...") - } - - // "Tar" files together (a .zip file with no compression) - if len(allFiles) > 1 || len(onlyFolders) > 0 { - var rootDir string - if len(onlyFolders) > 0 { - rootDir = filepath.Dir(onlyFolders[0]) - } else { - rootDir = filepath.Dir(onlyFiles[0]) - } - - file, err := os.Create(inputFile) - if err != nil { - mainStatus = "Access denied by operating system." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - return - } - - w := zip.NewWriter(file) - for i, path := range allFiles { - if !working { - w.Close() - file.Close() - os.Remove(inputFile) - mainStatus = "Operation cancelled by user." - mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff} - return - } - progressInfo = fmt.Sprintf("%d/%d", i, len(allFiles)) - progress = float32(i) / float32(len(allFiles)) - giu.Update() - if path == inputFile { - continue - } - - stat, _ := os.Stat(path) - header, _ := zip.FileInfoHeader(stat) - header.Name = strings.TrimPrefix(path, rootDir) - header.Name = filepath.ToSlash(header.Name) - header.Name = strings.TrimPrefix(header.Name, "/") - - if compress { - header.Method = zip.Deflate - } else { - header.Method = zip.Store - } - writer, _ := w.CreateHeader(header) - file, _ := os.Open(path) - io.Copy(writer, file) - file.Close() - } - w.Flush() - w.Close() - file.Close() - } - } - - if recombine { - popupStatus = s("Recombining file...") - total := 0 - - for { - _, err := os.Stat(fmt.Sprintf("%s.%d", inputFile, total)) - if err != nil { - break - } - total++ - } - - fout, _ := os.Create(inputFile) - for i := 0; i < total; i++ { - fin, _ := os.Open(fmt.Sprintf("%s.%d", inputFile, i)) - for { - data := make([]byte, 1048576) - read, err := fin.Read(data) - if err != nil { - break - } - data = data[:read] - fout.Write(data) - } - fin.Close() - progressInfo = fmt.Sprintf("%d/%d", i, total) - progress = float32(i) / float32(total) - giu.Update() - } - fout.Close() - progressInfo = "" - } - - stat, _ := os.Stat(inputFile) - total := stat.Size() - if mode == "decrypt" { - total -= 789 - } - - // XChaCha20's max message size is 256 GiB - if total > 256*1073741824 { - mainStatus = "Total size is larger than 256 GiB, XChaCha20's limit." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - return - } - - // Open input file in read-only mode - fin, err := os.Open(inputFile) - if err != nil { - mainStatus = "Access denied by operating system." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - return - } - - var fout *os.File - - // If encrypting, generate values; if decrypting, read values from file - if mode == "encrypt" { - popupStatus = s("Generating values...") - giu.Update() - - var err error - fout, err = os.Create(outputFile) - if err != nil { - mainStatus = "Access denied by operating system." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - return - } - - // Generate random cryptography values - salt = make([]byte, 16) - hkdfSalt = make([]byte, 32) - serpentSalt = make([]byte, 16) - nonce = make([]byte, 24) - - // Write version to file - fout.Write(rsEncode(rs5, []byte(version))) - - // Encode the length of the metadata with Reed-Solomon - metadataLength := []byte(fmt.Sprintf("%05d", len(metadata))) - metadataLength = rsEncode(rs5, metadataLength) - - // Write the length of the metadata to file - fout.Write(metadataLength) - - // Reed-Solomon-encode the metadata and write to file - for _, i := range []byte(metadata) { - fout.Write(rsEncode(rs1, []byte{i})) - } - - flags := make([]byte, 6) - if fast { - flags[0] = 1 - } - if paranoid { - flags[1] = 1 - } - if len(keyfiles) > 0 { - flags[2] = 1 - } - if reedsolo { - flags[3] = 1 - } - if total%1048576 >= 1048448 { - flags[4] = 1 - } - if keyfileOrderMatters { - flags[5] = 1 - } - flags = rsEncode(rs6, flags) - fout.Write(flags) - - // Fill salts and nonce with Go's CSPRNG - rand.Read(salt) - rand.Read(hkdfSalt) - rand.Read(serpentSalt) - rand.Read(nonce) - - // Encode salt with Reed-Solomon and write to file - _salt := rsEncode(rs16, salt) - fout.Write(_salt) - - // Encode HKDF salt with Reed-Solomon and write to file - _hkdfSalt := rsEncode(rs32, hkdfSalt) - fout.Write(_hkdfSalt) - - // Encode Serpent salt with Reed-Solomon and write to file - _serpentSalt := rsEncode(rs16, serpentSalt) - fout.Write(_serpentSalt) - - // Encode nonce with Reed-Solomon and write to file - _nonce := rsEncode(rs24, nonce) - fout.Write(_nonce) - - // Write placeholder for hash of key - fout.Write(make([]byte, 192)) - - // Write placeholder for hash of hash of keyfile - fout.Write(make([]byte, 96)) - - // Write placeholder for HMAC-BLAKE2b/HMAC-SHA3 of file - fout.Write(make([]byte, 192)) - } else { - var err1 error - var err2 error - var err3 error - var err4 error - var err5 error - var err6 error - var err7 error - var err8 error - var err9 error - var err10 error - - popupStatus = s("Reading values...") - giu.Update() - - version := make([]byte, 15) - fin.Read(version) - _, err1 = rsDecode(rs5, version) - - tmp := make([]byte, 15) - fin.Read(tmp) - tmp, err2 = rsDecode(rs5, tmp) - metadataLength, _ := strconv.Atoi(string(tmp)) - - fin.Read(make([]byte, metadataLength*3)) - - flags := make([]byte, 18) - fin.Read(flags) - flags, err3 = rsDecode(rs6, flags) - fast = flags[0] == 1 - paranoid = flags[1] == 1 - reedsolo = flags[3] == 1 - padded = flags[4] == 1 - - salt = make([]byte, 48) - fin.Read(salt) - salt, err4 = rsDecode(rs16, salt) - - hkdfSalt = make([]byte, 96) - fin.Read(hkdfSalt) - hkdfSalt, err5 = rsDecode(rs32, hkdfSalt) - - serpentSalt = make([]byte, 48) - fin.Read(serpentSalt) - serpentSalt, err6 = rsDecode(rs16, serpentSalt) - - nonce = make([]byte, 72) - fin.Read(nonce) - nonce, err7 = rsDecode(rs24, nonce) - - _keyHash = make([]byte, 192) - fin.Read(_keyHash) - _keyHash, err8 = rsDecode(rs64, _keyHash) - - _keyfileHash = make([]byte, 96) - fin.Read(_keyfileHash) - _keyfileHash, err9 = rsDecode(rs32, _keyfileHash) - - dataMac = make([]byte, 192) - fin.Read(dataMac) - dataMac, err10 = rsDecode(rs64, dataMac) - - // Is there a better way? - if err1 != nil || err2 != nil || err3 != nil || err4 != nil || err5 != nil || - err6 != nil || err7 != nil || err8 != nil || err9 != nil || err10 != nil { - if keep { - kept = true - } else { - mainStatus = "The header is corrupt and the input file cannot be decrypted." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - fin.Close() - return - } - } - } - - popupStatus = s("Deriving key...") - progress = 0 - progressInfo = "" - giu.Update() - - // Derive encryption/decryption keys and subkeys - var key []byte - if fast { - key = argon2.IDKey( - []byte(password), - salt, - 4, - 131072, - 4, - 32, - ) - } else if paranoid { - key = argon2.IDKey( - []byte(password), - salt, - 8, - 1048576, - 8, - 32, - ) - } else { - key = argon2.IDKey( - []byte(password), - salt, - 4, - 1048576, - 4, - 32, - ) - } - - if !working { - mainStatus = "Operation cancelled by user." - mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff} - if mode == "encrypt" && (len(allFiles) > 1 || len(onlyFolders) > 0) { - os.Remove(outputFile) - } - if recombine { - os.Remove(inputFile) - } - os.Remove(outputFile) - return - } - - if len(keyfiles) > 0 || keyfile { - if keyfileOrderMatters { - var keysum = sha3.New256() - for _, path := range keyfiles { - kin, _ := os.Open(path) - kstat, _ := os.Stat(path) - kbytes := make([]byte, kstat.Size()) - kin.Read(kbytes) - kin.Close() - keysum.Write(kbytes) - } - keyfileKey = keysum.Sum(nil) - keyfileSha3 := sha3.New256() - keyfileSha3.Write(keyfileKey) - keyfileHash = keyfileSha3.Sum(nil) - } else { - var keysum []byte - for _, path := range keyfiles { - kin, _ := os.Open(path) - kstat, _ := os.Stat(path) - kbytes := make([]byte, kstat.Size()) - kin.Read(kbytes) - kin.Close() - ksha3 := sha3.New256() - ksha3.Write(kbytes) - keyfileKey := ksha3.Sum(nil) - if keysum == nil { - keysum = keyfileKey - } else { - for i, j := range keyfileKey { - keysum[i] ^= j - } - } - } - keyfileKey = keysum - keyfileSha3 := sha3.New256() - keyfileSha3.Write(keysum) - keyfileHash = keyfileSha3.Sum(nil) - } - } - - sha3_512 := sha3.New512() - sha3_512.Write(key) - keyHash = sha3_512.Sum(nil) - - // Validate password and/or keyfiles - if mode == "decrypt" { - keyCorrect := true - keyfileCorrect := true - var tmp bool - - keyCorrect = !(subtle.ConstantTimeCompare(keyHash, _keyHash) == 0) - if keyfile { - keyfileCorrect = !(subtle.ConstantTimeCompare(keyfileHash, _keyfileHash) == 0) - tmp = !keyCorrect || !keyfileCorrect - } else { - tmp = !keyCorrect - } - - if tmp || keep { - if keep { - kept = true - } else { - fin.Close() - if !keyCorrect { - mainStatus = "The provided password is incorrect." - } else { - if keyfileOrderMatters { - mainStatus = "Incorrect keyfiles and/or order." - } else { - mainStatus = "Incorrect keyfiles." - } - } - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - key = nil - if recombine { - os.Remove(inputFile) - } - return - } - } - - var err error - fout, err = os.Create(outputFile) - if err != nil { - mainStatus = "Access denied by operating system." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - return - } - } - - if len(keyfiles) > 0 || keyfile { - // XOR key and keyfile - tmp := key - key = make([]byte, 32) - for i := range key { - key[i] = tmp[i] ^ keyfileKey[i] - } - } - - done := 0 - counter := 0 - startTime := time.Now() - chacha20, _ := chacha20.NewUnauthenticatedCipher(key, nonce) - - // Use HKDF-SHA3 to generate a subkey - var mac hash.Hash - subkey := make([]byte, 32) - hkdf := hkdf.New(sha3.New256, key, hkdfSalt, nil) - hkdf.Read(subkey) - if fast { - // Keyed BLAKE2b - mac, _ = blake2b.New512(subkey) - } else { - // HMAC-SHA3 - mac = hmac.New(sha3.New512, subkey) - } - - // Generate another subkey and cipher (not used unless paranoid mode is checked) - serpentKey := make([]byte, 32) - hkdf.Read(serpentKey) - _serpent, _ := serpent.NewCipher(serpentKey) - serpentCTR := cipher.NewCTR(_serpent, serpentSalt) - - for { - if !working { - mainStatus = "Operation cancelled by user." - mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff} - fin.Close() - fout.Close() - if mode == "encrypt" && (len(allFiles) > 1 || len(onlyFolders) > 0) { - os.Remove(outputFile) - } - if recombine { - os.Remove(inputFile) - } - os.Remove(outputFile) - return - } - - var data []byte - if mode == "decrypt" && reedsolo { - data = make([]byte, 1114112) - } else { - data = make([]byte, 1048576) - } - - size, err := fin.Read(data) - if err != nil { - break - } - data = data[:size] - _data := make([]byte, len(data)) - - // "Actual" encryption is done in the next couple of lines - if mode == "encrypt" { - if paranoid { - serpentCTR.XORKeyStream(_data, data) - copy(data, _data) - } - - chacha20.XORKeyStream(_data, data) - mac.Write(_data) - - if reedsolo { - copy(data, _data) - _data = nil - if len(data) == 1048576 { - for i := 0; i < 1048576; i += 128 { - tmp := data[i : i+128] - tmp = rsEncode(rs128, tmp) - _data = append(_data, tmp...) - } - } else { - chunks := math.Floor(float64(len(data)) / 128) - for i := 0; float64(i) < chunks; i++ { - tmp := data[i*128 : (i+1)*128] - tmp = rsEncode(rs128, tmp) - _data = append(_data, tmp...) - } - tmp := data[int(chunks*128):] - _data = append(_data, rsEncode(rs128, pad(tmp))...) - } - } - } else { - if reedsolo { - copy(_data, data) - data = nil - if len(_data) == 1114112 { - for i := 0; i < 1114112; i += 136 { - tmp := _data[i : i+136] - tmp, err = rsDecode(rs128, tmp) - if err != nil { - if keep { - kept = true - } else { - mainStatus = "The input file is too corrupted to decrypt." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - fin.Close() - fout.Close() - broken() - return - } - } - if i == 1113976 && done+1114112 >= int(total) && padded { - tmp = unpad(tmp) - } - data = append(data, tmp...) - } - } else { - chunks := len(_data)/136 - 1 - for i := 0; i < chunks; i++ { - tmp := _data[i*136 : (i+1)*136] - tmp, err = rsDecode(rs128, tmp) - if err != nil { - if keep { - kept = true - } else { - mainStatus = "The input file is too corrupted to decrypt." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - fin.Close() - fout.Close() - broken() - return - } - } - data = append(data, tmp...) - } - tmp := _data[int(chunks)*136:] - tmp, err = rsDecode(rs128, tmp) - if err != nil { - if keep { - kept = true - } else { - mainStatus = "The input file is too corrupted to decrypt." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - fin.Close() - fout.Close() - broken() - return - } - } - tmp = unpad(tmp) - data = append(data, tmp...) - } - _data = make([]byte, len(data)) - } - - mac.Write(data) - chacha20.XORKeyStream(_data, data) - - if paranoid { - copy(data, _data) - serpentCTR.XORKeyStream(_data, data) - } - } - fout.Write(_data) - - // Update stats - if mode == "decrypt" && reedsolo { - done += 1114112 - } else { - done += 1048576 - } - counter++ - progress = float32(done) / float32(total) - elapsed := float64(time.Since(startTime)) / math.Pow(10, 9) - speed := float64(done) / elapsed / math.Pow(10, 6) - eta := int(math.Floor(float64(total-int64(done)) / (speed * math.Pow(10, 6)))) - - if progress > 1 { - progress = 1 - } - - progressInfo = fmt.Sprintf("%.2f%%", progress*100) - popupStatus = fmt.Sprintf(s("Working at %.2f MB/s (ETA: %s)"), speed, humanize(eta)) - giu.Update() - } - - if mode == "encrypt" { - // Seek back to header and write important data - fout.Seek(int64(312+len(metadata)*3), 0) - fout.Write(rsEncode(rs64, keyHash)) - fout.Write(rsEncode(rs32, keyfileHash)) - fout.Write(rsEncode(rs64, mac.Sum(nil))) - } else { - // Validate the authenticity of decrypted data - if subtle.ConstantTimeCompare(mac.Sum(nil), dataMac) == 0 { - if keep { - kept = true - } else { - fin.Close() - fout.Close() - broken() - return - } - } - } - - fin.Close() - fout.Close() - - // Split files into chunks - if split { - var splitted []string - popupStatus = s("Splitting file...") - stat, _ := os.Stat(outputFile) - size := stat.Size() - finished := 0 - chunkSize, _ := strconv.Atoi(splitSize) - - // User can choose KiB, MiB, and GiB - if splitSelected == 0 { - chunkSize *= 1024 - } else if splitSelected == 1 { - chunkSize *= 1048576 - } else { - chunkSize *= 1073741824 - } - chunks := int(math.Ceil(float64(size) / float64(chunkSize))) - fin, _ := os.Open(outputFile) - - for i := 0; i < chunks; i++ { - fout, _ := os.Create(fmt.Sprintf("%s.%d", outputFile, i)) - done := 0 - for { - data := make([]byte, 1048576) - read, err := fin.Read(data) - if err != nil { - break - } - if !working { - fin.Close() - fout.Close() - mainStatus = "Operation cancelled by user." - mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff} - - // If user cancels, remove the unfinished files - for _, j := range splitted { - os.Remove(j) - } - os.Remove(fmt.Sprintf("%s.%d", outputFile, i)) - os.Remove(outputFile) - return - } - data = data[:read] - fout.Write(data) - done += read - if done >= chunkSize { - break - } - } - fout.Close() - finished++ - splitted = append(splitted, fmt.Sprintf("%s.%d", outputFile, i)) - progress = float32(finished) / float32(chunks) - progressInfo = fmt.Sprintf("%d/%d", finished, chunks) - giu.Update() - } - fin.Close() - os.Remove(outputFile) - } - - // Remove the temporary file used to combine a splitted Picocrypt volume - if recombine { - os.Remove(inputFile) - } - - // Delete the temporary zip file if user wishes - if len(allFiles) > 1 || len(onlyFolders) > 0 { - os.Remove(inputFile) - } - - if deleteWhenDone { - progressInfo = "" - popupStatus = s("Deleted files...") - giu.Update() - if mode == "decrypt" { - if recombine { - total := 0 - for { - _, err := os.Stat(fmt.Sprintf("%s.%d", inputFile, total)) - if err != nil { - break - } - os.Remove(fmt.Sprintf("%s.%d", inputFile, total)) - total++ - } - } else { - os.Remove(inputFile) - } - } else { - for _, i := range onlyFiles { - os.Remove(i) - } - for _, i := range onlyFolders { - os.RemoveAll(i) - } - } - } - - resetUI() - - // If user chose to keep a corrupted/modified file, let them know - if kept { - mainStatus = "The input file is corrupted and/or modified. Please be careful." - mainStatusColor = color.RGBA{0xff, 0xff, 0x00, 0xff} - } else { - mainStatus = "Completed." - mainStatusColor = color.RGBA{0x00, 0xff, 0x00, 0xff} - } - - // Clear UI state - working = false - kept = false - key = nil - popupStatus = s("Ready.") -} - -// This function is run if an issue occurs during decryption -func broken() { - mainStatus = "The input file is either corrupted or intentionally modified." - mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff} - if recombine { - os.Remove(inputFile) - } - os.Remove(outputFile) - giu.Update() -} - -// Generate file checksums -func generateChecksums(file string) { - fin, _ := os.Open(file) - - // Clear UI state - csMd5 = "" - csSha1 = "" - csSha256 = "" - csSha3 = "" - csBlake2b = "" - csBlake2s = "" - md5Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha1Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00} - blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00} - blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00} - csValidate = "" - - if md5Selected { - csMd5 = s("Calculating...") - } - if sha1Selected { - csSha1 = s("Calculating...") - } - if sha256Selected { - csSha256 = s("Calculating...") - } - if sha3Selected { - csSha3 = s("Calculating...") - } - if blake2bSelected { - csBlake2b = s("Calculating...") - } - if blake2sSelected { - csBlake2s = s("Calculating...") - } - - // Create the checksum objects - crcMd5 := md5.New() - crcSha1 := sha1.New() - crcSha256 := sha256.New() - crcSha3 := sha3.New256() - crcBlake2b, _ := blake2b.New256(nil) - crcBlake2s, _ := blake2s.New256(nil) - - stat, _ := os.Stat(file) - total := stat.Size() - var done int64 = 0 - for { - var data []byte - _data := make([]byte, 1048576) - size, err := fin.Read(_data) - if err != nil { - break - } - data = _data[:size] - - if md5Selected { - crcMd5.Write(data) - } - if sha1Selected { - crcSha1.Write(data) - } - if sha256Selected { - crcSha256.Write(data) - } - if sha3Selected { - crcSha3.Write(data) - } - if blake2bSelected { - crcBlake2b.Write(data) - } - if blake2sSelected { - crcBlake2s.Write(data) - } - - done += int64(size) - csProgress = float32(done) / float32(total) - giu.Update() - } - csProgress = 0 - if md5Selected { - csMd5 = hex.EncodeToString(crcMd5.Sum(nil)) - } - if sha1Selected { - csSha1 = hex.EncodeToString(crcSha1.Sum(nil)) - } - if sha256Selected { - csSha256 = hex.EncodeToString(crcSha256.Sum(nil)) - } - if sha3Selected { - csSha3 = hex.EncodeToString(crcSha3.Sum(nil)) - } - if blake2bSelected { - csBlake2b = hex.EncodeToString(crcBlake2b.Sum(nil)) - } - if blake2sSelected { - csBlake2s = hex.EncodeToString(crcBlake2s.Sum(nil)) - } - - fin.Close() - giu.Update() -} - -// Reset the UI to a clean state with nothing selected or checked -func resetUI() { - mode = "" - onlyFiles = nil - onlyFolders = nil - allFiles = nil - inputLabel = s("Drop files and folders into this window.") - password = "" - cPassword = "" - keyfiles = nil - keyfile = false - keyfileOrderMatters = false - keyfilePrompt = s("None selected.") - metadata = "" - metadataPrompt = "Metadata:" - metadataDisabled = false - keep = false - reedsolo = false - split = false - splitSize = "" - splitSelected = 1 - fast = false - deleteWhenDone = false - paranoid = false - compress = false - inputFile = "" - outputFile = "" - progress = 0 - progressInfo = "" - mainStatus = "Ready." - mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff} - giu.Update() -} - -// Reed-Solomon encoder -func rsEncode(rs *infectious.FEC, data []byte) []byte { - var res []byte - rs.Encode(data, func(s infectious.Share) { - res = append(res, s.DeepCopy().Data[0]) - }) - return res -} - -// Reed-Solomon decoder -func rsDecode(rs *infectious.FEC, data []byte) ([]byte, error) { - tmp := make([]infectious.Share, rs.Total()) - for i := 0; i < rs.Total(); i++ { - tmp[i] = infectious.Share{ - Number: i, - Data: []byte{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 -} - -// PKCS7 Pad (for use with Reed-Solomon, not for cryptographic purposes) -func pad(data []byte) []byte { - padLen := 128 - len(data)%128 - padding := bytes.Repeat([]byte{byte(padLen)}, padLen) - return append(data, padding...) -} - -// PKCS7 Unpad -func unpad(data []byte) []byte { - length := len(data) - padLen := int(data[length-1]) - return data[:length-padLen] -} - -func genPassword() string { - chars := "" - if genpassUpper { - chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - } - if genpassLower { - chars += "abcdefghijklmnopqrstuvwxyz" - } - if genpassNums { - chars += "1234567890" - } - if genpassSymbols { - chars += "-=!@#$^&()_+?" - } - if chars == "" { - return chars - } - tmp := make([]byte, genpassLength) - for i := 0; i < int(genpassLength); i++ { - j, _ := rand.Int(rand.Reader, new(big.Int).SetUint64(uint64(len(chars)))) - tmp[i] = chars[j.Int64()] - } - if genpassCopy { - clipboard.WriteAll(string(tmp)) - } - return string(tmp) -} - -// Convert seconds to HH:MM:SS -func humanize(seconds int) string { - hours := int(math.Floor(float64(seconds) / 3600)) - seconds %= 3600 - minutes := int(math.Floor(float64(seconds) / 60)) - seconds %= 60 - hours = int(math.Max(float64(hours), 0)) - minutes = int(math.Max(float64(minutes), 0)) - seconds = int(math.Max(float64(seconds), 0)) - return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) -} - -func s(term string) string { - for _, i := range locales { - if i.iso == selectedLocale { - for _, j := range locales { - if j.iso == "en" { - for k, l := range j.data { - if l == term { - return i.data[k] - } - } - } - } - - } - } - return term -} - -func main() { - // Parse locales - var obj map[string]json.RawMessage - json.Unmarshal(localeBytes, &obj) - for i := range obj { - var tmp []string - json.Unmarshal(obj[i], &tmp) - locales = append(locales, locale{ - iso: i, - data: tmp, - }) - } - - // Check system locale - for _, i := range locales { - tmp, err := jibber_jabber.DetectIETF() - if err == nil { - if strings.HasPrefix(tmp, i.iso) { - selectedLocale = i.iso - for j, k := range allLocales { - if k == i.iso { - languageSelected = int32(j) - } - } - } - } - } - - // Set a universal font - giu.SetDefaultFontFromBytes(font, 18) - - // Create the master window - window = giu.NewMasterWindow("Picocrypt", 442, 532, giu.MasterWindowFlagsNotResizable) - dialog.Init() - - // Set window icon - reader := bytes.NewReader(icon) - decoded, _ := png.Decode(reader) - window.SetIcon([]image.Image{decoded}) - - // Set callbacks - window.SetDropCallback(onDrop) - window.SetCloseCallback(func() bool { - return !working - }) - - // Set universal DPI - dpi = giu.Context.GetPlatform().GetContentScale() - - // Start a goroutine to check if a newer version is available - go func() { - v, err := http.Get("https://raw.githubusercontent.com/HACKERALERT/Picocrypt/main/internals/version.txt") - if err == nil { - body, err := io.ReadAll(v.Body) - v.Body.Close() - if err == nil { - if string(body[:5]) != version { - mainStatus = "A newer version is available." - mainStatusColor = color.RGBA{0x00, 0xff, 0x00, 0xff} - } - } - } - }() - - // Start the UI - window.Run(draw) -} diff --git a/src/unstable/README.md b/src/unstable/README.md deleted file mode 100644 index f671cbb..0000000 --- a/src/unstable/README.md +++ /dev/null @@ -1 +0,0 @@ -This is the latest and in-development version of Picocrypt. It might be unstable (and might not even work), as it will have new fixes and features, so I recommend you not use any file in here for production.