Picocrypt/src/Picocrypt.go

2475 lines
62 KiB
Go
Raw Normal View History

2021-05-30 07:23:22 +12:00
package main
/*
2021-09-27 11:55:00 +13:00
Picocrypt v1.19
2021-05-30 07:23:22 +12:00
Copyright (c) Evan Su (https://evansu.cc)
Released under a GNU GPL v3 License
https://github.com/HACKERALERT/Picocrypt
~ In cryptography we trust ~
*/
import (
2021-08-08 06:45:13 +12:00
_ "embed"
// Generic
2021-09-25 12:19:18 +12:00
"archive/zip"
"bytes"
"encoding/hex"
"encoding/json"
2021-08-08 06:45:13 +12:00
"fmt"
2021-09-25 12:19:18 +12:00
"hash"
"image"
"image/color"
"image/png"
2021-05-30 07:23:22 +12:00
"io"
"io/ioutil"
2021-05-30 07:23:22 +12:00
"math"
2021-08-10 11:51:25 +12:00
"math/big"
2021-05-30 07:23:22 +12:00
"net/http"
2021-09-25 12:19:18 +12:00
"os"
"os/exec"
2021-05-30 07:23:22 +12:00
"path/filepath"
2021-09-25 12:19:18 +12:00
"regexp"
"runtime"
2021-05-30 07:23:22 +12:00
"runtime/debug"
2021-09-25 12:19:18 +12:00
"strconv"
"strings"
"sync"
"syscall"
2021-09-25 12:19:18 +12:00
"time"
2021-08-08 06:45:13 +12:00
// Cryptography
"crypto/cipher"
2021-09-25 12:19:18 +12:00
"crypto/hmac"
2021-08-08 06:45:13 +12:00
"crypto/md5"
2021-09-25 12:19:18 +12:00
"crypto/rand"
2021-08-08 06:45:13 +12:00
"crypto/sha1"
"crypto/sha256"
2021-09-25 12:19:18 +12:00
"crypto/subtle"
"github.com/HACKERALERT/serpent"
2021-05-30 07:23:22 +12:00
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s"
2021-08-08 06:45:13 +12:00
"golang.org/x/crypto/chacha20"
2021-09-25 12:19:18 +12:00
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
// UI
"github.com/HACKERALERT/giu"
2021-08-08 06:45:13 +12:00
// Reed-Solomon
2021-09-25 12:19:18 +12:00
"github.com/HACKERALERT/infectious"
2021-08-08 06:45:13 +12:00
// Helpers
2021-09-25 12:19:18 +12:00
"github.com/HACKERALERT/clipboard"
"github.com/HACKERALERT/dialog"
"github.com/HACKERALERT/jibber_jabber"
"github.com/HACKERALERT/zxcvbn-go"
2021-05-30 07:23:22 +12:00
)
2021-09-25 12:19:18 +12:00
//go:embed icon.png
var icon []byte
2021-08-08 06:45:13 +12:00
2021-09-05 10:40:55 +12:00
//go:embed font.ttf
2021-08-08 06:45:13 +12:00
var font []byte
2021-09-05 10:40:55 +12:00
//go:embed sdelete64.exe
var sdelete64bytes []byte
2021-09-05 10:40:55 +12:00
//go:embed strings.json
var localeBytes []byte
2021-09-25 12:19:18 +12:00
// Localization
type locale struct {
iso string
2021-09-05 10:40:55 +12:00
data []string
}
var locales []locale
var selectedLocale = "en"
var allLocales = []string{
"en",
}
2021-08-08 06:45:13 +12:00
var languages = []string{
"English",
}
var languageSelected int32
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
// Generic variables
2021-09-27 11:59:47 +13:00
var version = "v1.19"
2021-09-25 12:19:18 +12:00
var dpi float32
var tab = 0
var mode string
var working bool
var recombine bool
var fill float32 = -0.0000001
var sdelete64path string
2021-09-25 12:19:18 +12:00
// 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."
2021-05-30 07:23:22 +12:00
var inputFile string
2021-09-25 12:19:18 +12:00
// Password variables
2021-05-30 07:23:22 +12:00
var password string
2021-09-25 12:19:18 +12:00
var cPassword string
var passwordStrength int
2021-08-11 15:35:35 +12:00
var passwordState = giu.InputTextFlagsPassword
var passwordStateLabel = "Show"
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
// Password generator variables
2021-08-11 15:35:35 +12:00
var showGenpass = false
var genpassCopy = true
var genpassLength int32 = 32
var genpassUpper = true
var genpassLower = true
var genpassNums = true
var genpassSymbols = true
2021-09-25 12:19:18 +12:00
// 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 shredTemp bool
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 encryptFilename 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
2021-05-30 07:23:22 +12:00
// Reed-Solomon encoders
2021-09-25 12:19:18 +12:00
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)
2021-05-30 07:23:22 +12:00
// File checksum generator variables
2021-09-25 12:19:18 +12:00
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
// Shredder variables
var shredding string = "Ready."
var shredPasses int32 = 4
var stopShredding bool
var shredProgress float32
var shredDone float32
var shredTotal float32
var shredOverlay string
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
func draw() {
2021-08-08 06:45:13 +12:00
giu.SingleWindow().Layout(
2021-09-25 12:19:18 +12:00
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]
shredding = s(shredding)
}).Size(w/dpi+26),
).Build()
giu.SetCursorPos(pos)
giu.TabBar().TabItems(
giu.TabItem(s("Encryption")).Layout(
giu.Custom(func() {
if giu.IsItemActive() {
tab = 0
}
}),
2021-08-11 15:35:35 +12:00
2021-09-25 12:19:18 +12:00
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(fill),
),
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()
}
}),
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
giu.Custom(func() {
if showKeyfile {
giu.PopupModal(s("Manage keyfiles:")).
Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize).Layout(
giu.Row(
giu.Label(s("Drop and drop your keyfiles.")),
),
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()
}
}),
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
giu.Custom(func() {
for _, i := range keyfiles {
giu.Row(
giu.Label(filepath.Base(i)),
giu.Button("Remove").OnClick(func() {
var tmp []string
for _, j := range keyfiles {
if j != i {
tmp = append(tmp, j)
2021-09-05 10:40:55 +12:00
}
2021-09-25 12:19:18 +12:00
}
keyfiles = tmp
}),
).Build()
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
}
}),
giu.Dummy(0, 200),
giu.Row(
giu.Button(s("Clear")).Size(150, 0).OnClick(func() {
keyfiles = nil
2021-09-05 10:40:55 +12:00
}),
2021-09-25 12:19:18 +12:00
giu.Tooltip(s("Remove all keyfiles.")),
giu.Button(s("Done")).Size(150, 0).OnClick(func() {
giu.CloseCurrentPopup()
showKeyfile = false
2021-09-05 10:40:55 +12:00
}),
2021-09-25 12:19:18 +12:00
),
).Build()
giu.OpenPopup(s("Manage keyfiles:"))
giu.Update()
}
}),
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
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
2021-08-08 06:45:13 +12:00
}),
2021-09-25 12:19:18 +12:00
giu.Button(s("Yes")).Size(100, 0).OnClick(func() {
giu.CloseCurrentPopup()
showConfirmation = false
showProgress = true
giu.Update()
go func() {
work()
2021-08-08 06:45:13 +12:00
working = false
2021-09-25 12:19:18 +12:00
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|giu.WindowFlagsNoTitleBar).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() {
w, _ := giu.GetAvailableRegion()
bw, _ := giu.CalcTextSize(s("Clear"))
p, _ := giu.GetWindowPadding()
bw += p * 2
dw := w - bw - p
2021-09-27 11:55:00 +13:00
giu.Dummy(float32(math.Max(float64(dw/dpi), float64(-bw/dpi-p))), 0).Build()
2021-09-25 12:19:18 +12:00
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()
2021-08-08 06:45:13 +12:00
}),
2021-09-25 12:19:18 +12:00
),
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
giu.Separator(),
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
giu.Style().SetDisabled(len(allFiles) == 0 && len(onlyFiles) == 0).To(
2021-08-08 06:45:13 +12:00
giu.Row(
2021-09-25 12:19:18 +12:00
giu.Label(s("Password:")),
2021-09-27 11:55:00 +13:00
giu.Dummy(-124, 0),
2021-09-25 12:19:18 +12:00
giu.Style().SetDisabled(mode == "decrypt" && !keyfile).To(
giu.Label(s("Keyfiles:")),
),
2021-08-08 06:45:13 +12:00
),
giu.Row(
2021-09-25 12:19:18 +12:00
giu.Button(s(passwordStateLabel)).Size(54, 0).OnClick(func() {
if passwordState == giu.InputTextFlagsPassword {
passwordState = giu.InputTextFlagsNone
passwordStateLabel = "Hide"
} else {
passwordState = giu.InputTextFlagsPassword
passwordStateLabel = "Show"
2021-09-05 10:40:55 +12:00
}
}),
2021-09-25 12:19:18 +12:00
giu.Button(s("Clear")).Size(54, 0).OnClick(func() {
2021-09-05 10:40:55 +12:00
password = ""
cPassword = ""
2021-09-25 12:19:18 +12:00
}),
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
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
2021-09-05 10:40:55 +12:00
}
2021-09-25 12:19:18 +12:00
passwordStrength = zxcvbn.PasswordStrength(password, nil).Score
giu.Update()
2021-08-10 11:51:25 +12:00
}),
2021-09-25 12:19:18 +12:00
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()
}),
),
),
),
2021-08-08 06:45:13 +12:00
),
giu.Row(
2021-09-25 12:19:18 +12:00
giu.InputText(&password).Flags(passwordState).Size(302/dpi).OnChange(func() {
passwordStrength = zxcvbn.PasswordStrength(password, nil).Score
2021-08-08 06:45:13 +12:00
}),
2021-09-25 12:19:18 +12:00
giu.Custom(func() {
c := giu.GetCanvas()
p := giu.GetCursorScreenPos()
2021-08-08 06:45:13 +12:00
var col color.RGBA
2021-09-25 12:19:18 +12:00
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}
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if password == "" || mode == "decrypt" {
col = color.RGBA{0xff, 0xff, 0xff, 0x00}
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
path := p.Add(image.Pt(
int(math.Round(float64(-20*dpi))),
2021-08-08 06:45:13 +12:00
int(math.Round(float64(12*dpi))),
))
2021-09-25 12:19:18 +12:00
c.PathArcTo(path, 6*dpi, -math.Pi/2, float32(passwordStrength+1)/5*2*math.Pi-math.Pi/2, -1)
c.PathStroke(col, false, 2)
2021-05-30 07:23:22 +12:00
}),
2021-09-25 12:19:18 +12:00
giu.Style().SetDisabled(true).To(
giu.InputText(&keyfilePrompt).Size(fill),
),
2021-08-08 06:45:13 +12:00
),
2021-09-25 12:19:18 +12:00
),
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
giu.Style().SetDisabled(password == "").To(
giu.Row(
giu.Style().SetDisabled(mode == "decrypt").To(
giu.Label(s("Confirm password:")),
),
2021-09-27 11:55:00 +13:00
giu.Dummy(-124, 0),
2021-09-25 12:19:18 +12:00
giu.Style().SetDisabled(true).To(
giu.Label(s("Custom Argon2:")),
),
),
),
giu.Style().SetDisabled(password == "").To(
giu.Row(
giu.Style().SetDisabled(mode == "decrypt").To(
2021-08-11 15:35:35 +12:00
giu.Row(
2021-09-25 12:19:18 +12:00
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}
2021-08-11 15:35:35 +12:00
}
2021-09-25 12:19:18 +12:00
if password == "" || cPassword == "" || mode == "decrypt" {
col = color.RGBA{0xff, 0xff, 0xff, 0x00}
2021-08-11 15:35:35 +12:00
}
2021-09-25 12:19:18 +12:00
path := p.Add(image.Pt(
int(math.Round(float64(-20*dpi))),
2021-08-11 15:35:35 +12:00
int(math.Round(float64(12*dpi))),
))
2021-09-25 12:19:18 +12:00
c.PathArcTo(path, 6*dpi, 0, 2*math.Pi, -1)
c.PathStroke(col, false, 2)
2021-08-11 15:35:35 +12:00
}),
2021-09-25 12:19:18 +12:00
),
),
giu.Style().SetDisabled(true).To(
2021-09-25 12:22:58 +12:00
giu.Button(s("W.I.P")).Size(fill, 0),
2021-09-25 12:19:18 +12:00
),
),
),
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
giu.Dummy(0, 3),
giu.Separator(),
giu.Dummy(0, 0),
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
giu.Style().SetDisabled(password == "" || (password != cPassword && mode == "encrypt")).To(
giu.Label(s(metadataPrompt)),
giu.Style().SetDisabled(metadataDisabled).To(
giu.InputText(&metadata).Size(fill),
),
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
giu.Label(s("Advanced:")),
giu.Custom(func() {
if mode != "decrypt" {
2021-08-08 06:45:13 +12:00
giu.Row(
2021-09-25 12:19:18 +12:00
giu.Checkbox(s("Shred temporary files"), &shredTemp),
2021-09-27 11:55:00 +13:00
giu.Dummy(-221, 0),
2021-09-25 12:19:18 +12:00
giu.Checkbox(s("Encode with Reed-Solomon"), &reedsolo),
2021-08-08 06:45:13 +12:00
).Build()
giu.Row(
2021-09-25 12:19:18 +12:00
giu.Checkbox(s("Use fast mode"), &fast),
2021-09-27 11:55:00 +13:00
giu.Dummy(-221, 0),
2021-09-25 12:19:18 +12:00
giu.Checkbox(s("Delete files when complete"), &deleteWhenDone),
2021-08-08 06:45:13 +12:00
).Build()
2021-09-05 10:40:55 +12:00
giu.Row(
2021-09-25 12:19:18 +12:00
giu.Checkbox(s("Use paranoid mode"), &paranoid),
2021-09-27 11:55:00 +13:00
giu.Dummy(-221, 0),
2021-09-25 12:19:18 +12:00
giu.Style().SetDisabled(!(len(allFiles) > 1 || len(onlyFolders) > 0)).To(
giu.Checkbox(s("Compress files"), &compress),
),
2021-09-05 10:40:55 +12:00
).Build()
2021-09-25 12:19:18 +12:00
giu.Row(
2021-09-25 12:22:58 +12:00
giu.Style().SetDisabled(true).To(
giu.Checkbox(s("Encrypt filename (W.I.P)"), &encryptFilename),
),
2021-09-27 11:55:00 +13:00
giu.Dummy(-221, 0),
2021-09-25 12:19:18 +12:00
giu.Checkbox(s("Split every"), &split),
2021-09-27 11:55:00 +13:00
giu.InputText(&splitSize).Size(55/dpi).Flags(giu.InputTextFlagsCharsHexadecimal).OnChange(func() {
2021-09-25 12:19:18 +12:00
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.Dummy(0, 52).Build()
2021-05-30 07:23:22 +12:00
}
}),
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
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(
2021-09-27 11:55:00 +13:00
giu.InputText(&outputFile).Size(dw / dpi / dpi).Flags(giu.InputTextFlagsReadOnly),
2021-09-25 12:19:18 +12:00
).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
}
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
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(fill, 34).OnClick(func() {
if keyfile && keyfiles == nil {
mainStatus = "Please select your keyfiles."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
return
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
_, err := os.Stat(outputFile)
if err == nil {
2021-08-08 06:45:13 +12:00
showConfirmation = true
giu.Update()
2021-09-25 12:19:18 +12:00
} else {
2021-08-08 06:45:13 +12:00
showProgress = true
giu.Update()
2021-09-25 12:19:18 +12:00
go func() {
2021-08-08 06:45:13 +12:00
work()
working = false
showProgress = false
debug.FreeOSMemory()
giu.Update()
}()
2021-05-30 07:23:22 +12:00
}
}),
2021-09-25 12:19:18 +12:00
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(fill).Flags(giu.InputTextFlagsReadOnly),
2021-08-08 06:45:13 +12:00
),
2021-05-30 07:23:22 +12:00
),
2021-09-25 12:19:18 +12:00
// 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(fill).Flags(giu.InputTextFlagsReadOnly),
2021-08-08 06:45:13 +12:00
),
2021-09-25 12:19:18 +12:00
),
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
// 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(fill).Flags(giu.InputTextFlagsReadOnly),
2021-08-08 06:45:13 +12:00
),
2021-09-25 12:19:18 +12:00
),
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
// 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(fill).Flags(giu.InputTextFlagsReadOnly),
2021-08-08 06:45:13 +12:00
),
2021-09-25 12:19:18 +12:00
),
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
// 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(fill).Flags(giu.InputTextFlagsReadOnly),
2021-08-08 06:45:13 +12:00
),
2021-09-25 12:19:18 +12:00
),
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
// 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(fill).Flags(giu.InputTextFlagsReadOnly),
2021-08-08 06:45:13 +12:00
),
2021-09-25 12:19:18 +12:00
),
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
giu.Dummy(0, 23),
// 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()
2021-09-05 10:40:55 +12:00
}).Build()
}),
2021-09-25 12:19:18 +12:00
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()
2021-05-30 07:23:22 +12:00
}),
2021-09-25 12:19:18 +12:00
),
giu.Style().SetDisabled(true).To(
giu.InputText(&csValidate).Size(fill),
2021-05-30 07:23:22 +12:00
),
2021-09-25 12:19:18 +12:00
// Progress bar
giu.Label(s("Progress:")),
giu.ProgressBar(csProgress).Size(fill, 0),
),
giu.TabItem(s("Shredder")).Layout(
giu.Custom(func() {
if giu.IsItemActive() {
tab = 2
}
}),
giu.Label(s("Drop files and folders here to shred them.")),
giu.Custom(func() {
if runtime.GOOS == "darwin" {
giu.Label(s("Number of passes: Not supported on macOS")).Build()
} else {
2021-09-05 10:40:55 +12:00
giu.Row(
2021-09-25 12:19:18 +12:00
giu.Label(s("Number of passes:")),
giu.SliderInt(&shredPasses, 1, 32).Size(fill),
2021-09-05 10:40:55 +12:00
).Build()
2021-09-25 12:19:18 +12:00
}
}),
giu.Dummy(0, -50),
giu.Custom(func() {
w, _ := giu.GetAvailableRegion()
bw, _ := giu.CalcTextSize(s("Cancel"))
padding, _ := giu.GetWindowPadding()
bw += 2 * padding
size := w - bw - padding
giu.Row(
giu.ProgressBar(shredProgress).Overlay(shredOverlay).Size(size/dpi, 0),
giu.Button(s("Cancel")).Size(bw/dpi, 0).OnClick(func() {
stopShredding = true
shredding = s("Ready.")
shredProgress = 0
shredOverlay = ""
}),
).Build()
}),
giu.Custom(func() {
if len(shredding) > 60 {
shredding = "....." + shredding[len(shredding)-50:]
}
giu.Label(shredding).Wrapped(true).Build()
}),
),
giu.TabItem(s("About")).Layout(
giu.Custom(func() {
if giu.IsItemActive() {
tab = 3
}
}),
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()
}),
2021-05-30 07:23:22 +12:00
)
}
2021-09-25 12:19:18 +12:00
func onDrop(names []string) {
if tab == 1 {
go generateChecksums(names[0])
return
}
if tab == 2 {
go shred(names, true)
2021-09-25 12:19:18 +12:00
return
}
if showKeyfile {
keyfiles = append(keyfiles, names...)
tmp := []string{}
for _, i := range keyfiles {
duplicate := false
for _, j := range tmp {
if i == j {
duplicate = true
2021-09-05 10:40:55 +12:00
}
}
2021-09-25 12:19:18 +12:00
stat, _ := os.Stat(i)
if !duplicate && !stat.IsDir() {
tmp = append(tmp, i)
}
2021-09-05 10:40:55 +12:00
}
2021-09-25 12:19:18 +12:00
keyfiles = tmp
if len(keyfiles) == 1 {
keyfilePrompt = s("Using 1 keyfile.")
}
keyfilePrompt = fmt.Sprintf(s("Using %d keyfiles."), len(keyfiles))
return
}
2021-09-05 10:40:55 +12:00
2021-09-25 12:19:18 +12:00
// Clear variables
recombine = false
onlyFiles = nil
onlyFolders = nil
allFiles = nil
files, folders := 0, 0
resetUI()
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
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]
}
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
// 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])
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
// 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)
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
if err == nil {
metadataLength, _ := strconv.Atoi(string(tmp))
tmp = make([]byte, metadataLength*3)
2021-05-30 07:23:22 +12:00
fin.Read(tmp)
2021-09-25 12:19:18 +12:00
metadata = ""
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
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
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
metadata += string(t)
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
} else {
metadata = s("Metadata is corrupted.")
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
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
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
if flags[2] == 1 {
keyfile = true
keyfilePrompt = s("Keyfiles required.")
} else {
keyfilePrompt = s("Not applicable.")
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
if flags[5] == 1 {
keyfileOrderMatters = true
}
} else {
mode = "encrypt"
inputLabel = name + s(" (will encrypt)")
inputFile = names[0]
outputFile = names[0] + ".pcv"
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
// Add the file
onlyFiles = append(onlyFiles, names[0])
inputFile = names[0]
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
} else {
mode = "encrypt"
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
// 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)
2021-05-30 07:23:22 +12:00
}
}
2021-09-25 12:19:18 +12:00
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
})
}
}
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
func work() {
popupStatus = s("Starting...")
mainStatus = "Working..."
mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
2021-05-30 07:23:22 +12:00
working = true
2021-08-08 06:45:13 +12:00
padded := false
2021-09-25 12:19:18 +12:00
2021-05-30 07:23:22 +12:00
var salt []byte
2021-08-08 06:45:13 +12:00
var hkdfSalt []byte
var serpentSalt []byte
2021-05-30 07:23:22 +12:00
var nonce []byte
var keyHash []byte
var _keyHash []byte
2021-09-25 12:19:18 +12:00
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 {
2021-08-08 06:45:13 +12:00
var rootDir string
2021-09-25 12:19:18 +12:00
if len(onlyFolders) > 0 {
2021-08-08 06:45:13 +12:00
rootDir = filepath.Dir(onlyFolders[0])
2021-09-25 12:19:18 +12:00
} else {
2021-08-08 06:45:13 +12:00
rootDir = filepath.Dir(onlyFiles[0])
}
2021-09-25 12:19:18 +12:00
file, err := os.Create(inputFile)
if err != nil {
mainStatus = "Access denied by operating system."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
return
}
2021-05-30 07:23:22 +12:00
w := zip.NewWriter(file)
2021-09-25 12:19:18 +12:00
for i, path := range allFiles {
if !working {
2021-08-08 06:45:13 +12:00
w.Close()
file.Close()
os.Remove(inputFile)
2021-09-25 12:19:18 +12:00
mainStatus = "Operation cancelled by user."
mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
2021-08-08 06:45:13 +12:00
return
}
2021-09-25 12:19:18 +12:00
progressInfo = fmt.Sprintf("%d/%d", i, len(allFiles))
progress = float32(i) / float32(len(allFiles))
2021-08-08 06:45:13 +12:00
giu.Update()
2021-09-25 12:19:18 +12:00
if path == inputFile {
2021-05-30 07:23:22 +12:00
continue
}
2021-09-25 12:19:18 +12:00
stat, _ := os.Stat(path)
header, _ := zip.FileInfoHeader(stat)
header.Name = strings.TrimPrefix(path, rootDir)
// Windows requires forward slashes in a .zip file
if runtime.GOOS == "windows" {
header.Name = strings.ReplaceAll(header.Name, "\\", "/")
header.Name = strings.TrimPrefix(header.Name, "/")
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if compress {
header.Method = zip.Deflate
} else {
header.Method = zip.Store
}
writer, _ := w.CreateHeader(header)
file, _ := os.Open(path)
io.Copy(writer, file)
2021-05-30 07:23:22 +12:00
file.Close()
}
w.Flush()
w.Close()
file.Close()
}
}
2021-09-25 12:19:18 +12:00
if recombine {
popupStatus = s("Recombining file...")
2021-05-30 07:23:22 +12:00
total := 0
2021-09-25 12:19:18 +12:00
for {
_, err := os.Stat(fmt.Sprintf("%s.%d", inputFile, total))
if err != nil {
2021-05-30 07:23:22 +12:00
break
}
total++
}
2021-09-25 12:19:18 +12:00
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 {
2021-05-30 07:23:22 +12:00
break
}
data = data[:read]
fout.Write(data)
}
fin.Close()
2021-09-25 12:19:18 +12:00
progressInfo = fmt.Sprintf("%d/%d", i, total)
progress = float32(i) / float32(total)
2021-08-08 06:45:13 +12:00
giu.Update()
2021-05-30 07:23:22 +12:00
}
fout.Close()
progressInfo = ""
}
2021-09-25 12:19:18 +12:00
stat, _ := os.Stat(inputFile)
2021-05-30 07:23:22 +12:00
total := stat.Size()
2021-09-25 12:19:18 +12:00
if mode == "decrypt" {
2021-08-08 06:45:13 +12:00
total -= 789
}
// XChaCha20's max message size is 256 GiB
2021-09-25 12:19:18 +12:00
if total > 256*1073741824 {
mainStatus = "Total size is larger than 256 GiB, XChaCha20's limit."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
2021-08-08 06:45:13 +12:00
return
}
2021-09-25 12:19:18 +12:00
2021-05-30 07:23:22 +12:00
// Open input file in read-only mode
2021-09-25 12:19:18 +12:00
fin, err := os.Open(inputFile)
if err != nil {
mainStatus = "Access denied by operating system."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
return
}
2021-05-30 07:23:22 +12:00
var fout *os.File
2021-09-25 12:19:18 +12:00
// If encrypting, generate values; if decrypting, read values from file
if mode == "encrypt" {
popupStatus = s("Generating values...")
2021-08-08 06:45:13 +12:00
giu.Update()
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
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)
2021-05-30 07:23:22 +12:00
// Write version to file
2021-09-25 12:19:18 +12:00
fout.Write(rsEncode(rs5, []byte(version)))
2021-05-30 07:23:22 +12:00
// Encode the length of the metadata with Reed-Solomon
2021-09-25 12:19:18 +12:00
metadataLength := []byte(fmt.Sprintf("%05d", len(metadata)))
metadataLength = rsEncode(rs5, metadataLength)
2021-05-30 07:23:22 +12:00
// Write the length of the metadata to file
fout.Write(metadataLength)
2021-08-08 06:45:13 +12:00
// Reed-Solomon-encode the metadata and write to file
2021-09-25 12:19:18 +12:00
for _, i := range []byte(metadata) {
fout.Write(rsEncode(rs1, []byte{i}))
2021-08-08 06:45:13 +12:00
}
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
flags := make([]byte, 6)
if fast {
2021-05-30 07:23:22 +12:00
flags[0] = 1
}
2021-09-25 12:19:18 +12:00
if paranoid {
2021-05-30 07:23:22 +12:00
flags[1] = 1
}
2021-09-25 12:19:18 +12:00
if len(keyfiles) > 0 {
2021-08-08 06:45:13 +12:00
flags[2] = 1
}
2021-09-25 12:19:18 +12:00
if reedsolo {
2021-08-08 06:45:13 +12:00
flags[3] = 1
}
2021-09-25 12:19:18 +12:00
if total%1048576 >= 1048448 {
2021-08-08 06:45:13 +12:00
flags[4] = 1
}
2021-09-25 12:19:18 +12:00
if keyfileOrderMatters {
2021-09-05 10:40:55 +12:00
flags[5] = 1
}
2021-09-25 12:19:18 +12:00
flags = rsEncode(rs6, flags)
2021-05-30 07:23:22 +12:00
fout.Write(flags)
2021-08-08 06:45:13 +12:00
// Fill salts and nonce with Go's CSPRNG
2021-05-30 07:23:22 +12:00
rand.Read(salt)
2021-08-08 06:45:13 +12:00
rand.Read(hkdfSalt)
rand.Read(serpentSalt)
2021-05-30 07:23:22 +12:00
rand.Read(nonce)
// Encode salt with Reed-Solomon and write to file
2021-09-25 12:19:18 +12:00
_salt := rsEncode(rs16, salt)
2021-05-30 07:23:22 +12:00
fout.Write(_salt)
2021-08-08 06:45:13 +12:00
// Encode HKDF salt with Reed-Solomon and write to file
2021-09-25 12:19:18 +12:00
_hkdfSalt := rsEncode(rs32, hkdfSalt)
2021-08-08 06:45:13 +12:00
fout.Write(_hkdfSalt)
// Encode Serpent salt with Reed-Solomon and write to file
2021-09-25 12:19:18 +12:00
_serpentSalt := rsEncode(rs16, serpentSalt)
2021-08-08 06:45:13 +12:00
fout.Write(_serpentSalt)
2021-05-30 07:23:22 +12:00
// Encode nonce with Reed-Solomon and write to file
2021-09-25 12:19:18 +12:00
_nonce := rsEncode(rs24, nonce)
2021-08-08 06:45:13 +12:00
fout.Write(_nonce)
2021-09-25 12:19:18 +12:00
2021-05-30 07:23:22 +12:00
// Write placeholder for hash of key
2021-09-25 12:19:18 +12:00
fout.Write(make([]byte, 192))
2021-05-30 07:23:22 +12:00
// Write placeholder for hash of hash of keyfile
2021-09-25 12:19:18 +12:00
fout.Write(make([]byte, 96))
2021-05-30 07:23:22 +12:00
2021-08-08 06:45:13 +12:00
// Write placeholder for HMAC-BLAKE2b/HMAC-SHA3 of file
2021-09-25 12:19:18 +12:00
fout.Write(make([]byte, 192))
} else {
2021-08-08 06:45:13 +12:00
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
2021-09-25 12:19:18 +12:00
popupStatus = s("Reading values...")
2021-08-08 06:45:13 +12:00
giu.Update()
2021-09-25 12:19:18 +12:00
version := make([]byte, 15)
2021-05-30 07:23:22 +12:00
fin.Read(version)
2021-09-25 12:19:18 +12:00
_, err1 = rsDecode(rs5, version)
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
tmp := make([]byte, 15)
2021-05-30 07:23:22 +12:00
fin.Read(tmp)
2021-09-25 12:19:18 +12:00
tmp, err2 = rsDecode(rs5, tmp)
metadataLength, _ := strconv.Atoi(string(tmp))
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
fin.Read(make([]byte, metadataLength*3))
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
flags := make([]byte, 18)
2021-05-30 07:23:22 +12:00
fin.Read(flags)
2021-09-25 12:19:18 +12:00
flags, err3 = rsDecode(rs6, flags)
fast = flags[0] == 1
paranoid = flags[1] == 1
reedsolo = flags[3] == 1
padded = flags[4] == 1
2021-05-30 07:23:22 +12:00
2021-09-25 12:19:18 +12:00
salt = make([]byte, 48)
2021-05-30 07:23:22 +12:00
fin.Read(salt)
2021-09-25 12:19:18 +12:00
salt, err4 = rsDecode(rs16, salt)
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
hkdfSalt = make([]byte, 96)
2021-08-08 06:45:13 +12:00
fin.Read(hkdfSalt)
2021-09-25 12:19:18 +12:00
hkdfSalt, err5 = rsDecode(rs32, hkdfSalt)
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
serpentSalt = make([]byte, 48)
2021-08-08 06:45:13 +12:00
fin.Read(serpentSalt)
2021-09-25 12:19:18 +12:00
serpentSalt, err6 = rsDecode(rs16, serpentSalt)
nonce = make([]byte, 72)
2021-05-30 07:23:22 +12:00
fin.Read(nonce)
2021-09-25 12:19:18 +12:00
nonce, err7 = rsDecode(rs24, nonce)
_keyHash = make([]byte, 192)
2021-05-30 07:23:22 +12:00
fin.Read(_keyHash)
2021-09-25 12:19:18 +12:00
_keyHash, err8 = rsDecode(rs64, _keyHash)
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
_keyfileHash = make([]byte, 96)
fin.Read(_keyfileHash)
_keyfileHash, err9 = rsDecode(rs32, _keyfileHash)
dataMac = make([]byte, 192)
fin.Read(dataMac)
dataMac, err10 = rsDecode(rs64, dataMac)
2021-08-08 06:45:13 +12:00
// Is there a better way?
2021-09-25 12:19:18 +12:00
if err1 != nil || err2 != nil || err3 != nil || err4 != nil || err5 != nil ||
err6 != nil || err7 != nil || err8 != nil || err9 != nil || err10 != nil {
if keep {
2021-08-08 06:45:13 +12:00
kept = true
2021-09-25 12:19:18 +12:00
} else {
mainStatus = "The header is corrupt and the input file cannot be decrypted."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
2021-08-08 06:45:13 +12:00
fin.Close()
return
}
}
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
popupStatus = s("Deriving key...")
2021-05-30 07:23:22 +12:00
progress = 0
2021-08-08 06:45:13 +12:00
progressInfo = ""
giu.Update()
2021-09-25 12:19:18 +12:00
// Derive encryption/decryption keys and subkeys
2021-05-30 07:23:22 +12:00
var key []byte
2021-09-25 12:19:18 +12:00
if fast {
2021-05-30 07:23:22 +12:00
key = argon2.IDKey(
[]byte(password),
salt,
4,
131072,
4,
32,
2021-09-25 12:19:18 +12:00
)
} else if paranoid {
2021-05-30 07:23:22 +12:00
key = argon2.IDKey(
[]byte(password),
salt,
8,
1048576,
8,
32,
2021-09-25 12:19:18 +12:00
)
} else {
2021-09-05 10:40:55 +12:00
key = argon2.IDKey(
[]byte(password),
salt,
4,
1048576,
4,
32,
2021-09-25 12:19:18 +12:00
)
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
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)
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if recombine {
2021-08-08 06:45:13 +12:00
os.Remove(inputFile)
}
os.Remove(outputFile)
2021-05-30 07:23:22 +12:00
return
}
2021-09-25 12:19:18 +12:00
if len(keyfiles) > 0 || keyfile {
if keyfileOrderMatters {
2021-09-05 10:40:55 +12:00
var keysum = sha3.New256()
2021-09-25 12:19:18 +12:00
for _, path := range keyfiles {
kin, _ := os.Open(path)
kstat, _ := os.Stat(path)
kbytes := make([]byte, kstat.Size())
2021-09-05 10:40:55 +12:00
kin.Read(kbytes)
kin.Close()
keysum.Write(kbytes)
}
2021-09-25 12:19:18 +12:00
keyfileKey = keysum.Sum(nil)
keyfileSha3 := sha3.New256()
keyfileSha3.Write(keyfileKey)
keyfileHash = keyfileSha3.Sum(nil)
} else {
2021-09-05 10:40:55 +12:00
var keysum []byte
2021-09-25 12:19:18 +12:00
for _, path := range keyfiles {
kin, _ := os.Open(path)
kstat, _ := os.Stat(path)
kbytes := make([]byte, kstat.Size())
2021-09-05 10:40:55 +12:00
kin.Read(kbytes)
kin.Close()
ksha3 := sha3.New256()
ksha3.Write(kbytes)
2021-09-25 12:19:18 +12:00
keyfileKey := ksha3.Sum(nil)
if keysum == nil {
keysum = keyfileKey
} else {
for i, j := range keyfileKey {
2021-09-05 10:40:55 +12:00
keysum[i] ^= j
}
}
}
2021-09-25 12:19:18 +12:00
keyfileKey = keysum
keyfileSha3 := sha3.New256()
keyfileSha3.Write(keysum)
keyfileHash = keyfileSha3.Sum(nil)
2021-09-05 10:40:55 +12:00
}
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
2021-05-30 07:23:22 +12:00
sha3_512 := sha3.New512()
sha3_512.Write(key)
keyHash = sha3_512.Sum(nil)
2021-09-25 12:19:18 +12:00
// Validate password and/or keyfiles
if mode == "decrypt" {
2021-05-30 07:23:22 +12:00
keyCorrect := true
keyfileCorrect := true
var tmp bool
2021-09-25 12:19:18 +12:00
keyCorrect = !(subtle.ConstantTimeCompare(keyHash, _keyHash) == 0)
if keyfile {
keyfileCorrect = !(subtle.ConstantTimeCompare(keyfileHash, _keyfileHash) == 0)
tmp = !keyCorrect || !keyfileCorrect
} else {
2021-05-30 07:23:22 +12:00
tmp = !keyCorrect
}
2021-09-25 12:19:18 +12:00
if tmp || keep {
if keep {
2021-05-30 07:23:22 +12:00
kept = true
2021-09-25 12:19:18 +12:00
} else {
2021-05-30 07:23:22 +12:00
fin.Close()
2021-09-25 12:19:18 +12:00
if !keyCorrect {
mainStatus = "The provided password is incorrect."
} else {
if keyfileOrderMatters {
mainStatus = "Incorrect keyfiles and/or order."
} else {
mainStatus = "Incorrect keyfiles."
2021-09-05 10:40:55 +12:00
}
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
2021-05-30 07:23:22 +12:00
key = nil
2021-09-25 12:19:18 +12:00
if recombine {
2021-05-30 07:23:22 +12:00
os.Remove(inputFile)
}
return
}
}
2021-09-25 12:19:18 +12:00
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
}
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
if len(keyfiles) > 0 || keyfile {
2021-05-30 07:23:22 +12:00
// XOR key and keyfile
tmp := key
2021-09-25 12:19:18 +12:00
key = make([]byte, 32)
for i := range key {
key[i] = tmp[i] ^ keyfileKey[i]
2021-05-30 07:23:22 +12:00
}
}
2021-09-25 12:19:18 +12:00
2021-05-30 07:23:22 +12:00
done := 0
counter := 0
startTime := time.Now()
2021-09-25 12:19:18 +12:00
chacha20, _ := chacha20.NewUnauthenticatedCipher(key, nonce)
2021-05-30 07:23:22 +12:00
2021-08-08 06:45:13 +12:00
// Use HKDF-SHA3 to generate a subkey
var mac hash.Hash
2021-09-25 12:19:18 +12:00
subkey := make([]byte, 32)
hkdf := hkdf.New(sha3.New256, key, hkdfSalt, nil)
2021-08-08 06:45:13 +12:00
hkdf.Read(subkey)
2021-09-25 12:19:18 +12:00
if fast {
2021-08-08 06:45:13 +12:00
// Keyed BLAKE2b
2021-09-25 12:19:18 +12:00
mac, _ = blake2b.New512(subkey)
} else {
2021-08-08 06:45:13 +12:00
// HMAC-SHA3
2021-09-25 12:19:18 +12:00
mac = hmac.New(sha3.New512, subkey)
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// Generate another subkey and cipher (not used unless paranoid mode is checked)
2021-09-25 12:19:18 +12:00
serpentKey := make([]byte, 32)
2021-08-08 06:45:13 +12:00
hkdf.Read(serpentKey)
2021-09-25 12:19:18 +12:00
_serpent, _ := serpent.NewCipher(serpentKey)
serpentCTR := cipher.NewCTR(_serpent, serpentSalt)
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
for {
if !working {
mainStatus = "Operation cancelled by user."
mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
2021-05-30 07:23:22 +12:00
fin.Close()
fout.Close()
2021-09-25 12:19:18 +12:00
if mode == "encrypt" && (len(allFiles) > 1 || len(onlyFolders) > 0) {
os.Remove(outputFile)
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if recombine {
2021-08-08 06:45:13 +12:00
os.Remove(inputFile)
}
2021-05-30 07:23:22 +12:00
os.Remove(outputFile)
return
}
2021-08-08 06:45:13 +12:00
2021-05-30 07:23:22 +12:00
var data []byte
2021-09-25 12:19:18 +12:00
if mode == "decrypt" && reedsolo {
data = make([]byte, 1114112)
} else {
data = make([]byte, 1048576)
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
size, err := fin.Read(data)
if err != nil {
2021-05-30 07:23:22 +12:00
break
}
2021-08-08 06:45:13 +12:00
data = data[:size]
2021-09-25 12:19:18 +12:00
_data := make([]byte, len(data))
2021-05-30 07:23:22 +12:00
2021-08-08 06:45:13 +12:00
// "Actual" encryption is done in the next couple of lines
2021-09-25 12:19:18 +12:00
if mode == "encrypt" {
if paranoid {
serpentCTR.XORKeyStream(_data, data)
copy(data, _data)
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
chacha20.XORKeyStream(_data, data)
2021-08-08 06:45:13 +12:00
mac.Write(_data)
2021-09-25 12:19:18 +12:00
if reedsolo {
copy(data, _data)
2021-08-08 06:45:13 +12:00
_data = nil
2021-09-25 12:19:18 +12:00
if len(data) == 1048576 {
for i := 0; i < 1048576; i += 128 {
tmp := data[i : i+128]
tmp = rsEncode(rs128, tmp)
_data = append(_data, tmp...)
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
} 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...)
2021-08-08 06:45:13 +12:00
}
tmp := data[int(chunks*128):]
2021-09-25 12:19:18 +12:00
_data = append(_data, rsEncode(rs128, pad(tmp))...)
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
} else {
if reedsolo {
copy(_data, data)
2021-08-08 06:45:13 +12:00
data = nil
2021-09-25 12:19:18 +12:00
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 {
2021-08-08 06:45:13 +12:00
kept = true
2021-09-25 12:19:18 +12:00
} else {
mainStatus = "The input file is too corrupted to decrypt."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
2021-08-08 06:45:13 +12:00
fin.Close()
fout.Close()
broken()
return
}
}
2021-09-25 12:19:18 +12:00
if i == 1113976 && done+1114112 >= int(total) && padded {
2021-08-08 06:45:13 +12:00
tmp = unpad(tmp)
}
2021-09-25 12:19:18 +12:00
data = append(data, tmp...)
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
} 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 {
2021-08-08 06:45:13 +12:00
kept = true
2021-09-25 12:19:18 +12:00
} else {
mainStatus = "The input file is too corrupted to decrypt."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
2021-08-08 06:45:13 +12:00
fin.Close()
fout.Close()
broken()
return
}
}
2021-09-25 12:19:18 +12:00
data = append(data, tmp...)
2021-08-08 06:45:13 +12:00
}
tmp := _data[int(chunks)*136:]
2021-09-25 12:19:18 +12:00
tmp, err = rsDecode(rs128, tmp)
if err != nil {
if keep {
2021-08-08 06:45:13 +12:00
kept = true
2021-09-25 12:19:18 +12:00
} else {
mainStatus = "The input file is too corrupted to decrypt."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
2021-08-08 06:45:13 +12:00
fin.Close()
fout.Close()
broken()
return
}
}
tmp = unpad(tmp)
2021-09-25 12:19:18 +12:00
data = append(data, tmp...)
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
_data = make([]byte, len(data))
2021-08-08 06:45:13 +12:00
}
mac.Write(data)
2021-09-25 12:19:18 +12:00
chacha20.XORKeyStream(_data, data)
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
if paranoid {
copy(data, _data)
serpentCTR.XORKeyStream(_data, data)
2021-05-30 07:23:22 +12:00
}
}
2021-08-08 06:45:13 +12:00
fout.Write(_data)
2021-09-25 12:19:18 +12:00
2021-08-08 06:45:13 +12:00
// Update stats
2021-09-25 12:19:18 +12:00
if mode == "decrypt" && reedsolo {
2021-08-08 06:45:13 +12:00
done += 1114112
2021-09-25 12:19:18 +12:00
} else {
2021-08-08 06:45:13 +12:00
done += 1048576
}
2021-05-30 07:23:22 +12:00
counter++
2021-09-25 12:19:18 +12:00
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))))
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
if progress > 1 {
2021-08-08 06:45:13 +12:00
progress = 1
}
2021-09-25 12:19:18 +12:00
progressInfo = fmt.Sprintf("%.2f%%", progress*100)
popupStatus = fmt.Sprintf(s("Working at %.2f MB/s (ETA: %s)"), speed, humanize(eta))
2021-08-08 06:45:13 +12:00
giu.Update()
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
if mode == "encrypt" {
2021-08-08 06:45:13 +12:00
// Seek back to header and write important data
2021-09-25 12:19:18 +12:00
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 {
2021-08-08 06:45:13 +12:00
// Validate the authenticity of decrypted data
2021-09-25 12:19:18 +12:00
if subtle.ConstantTimeCompare(mac.Sum(nil), dataMac) == 0 {
if keep {
2021-08-08 06:45:13 +12:00
kept = true
2021-09-25 12:19:18 +12:00
} else {
2021-08-08 06:45:13 +12:00
fin.Close()
fout.Close()
broken()
return
2021-05-30 07:23:22 +12:00
}
}
}
2021-08-08 06:45:13 +12:00
2021-05-30 07:23:22 +12:00
fin.Close()
fout.Close()
2021-08-08 06:45:13 +12:00
// Split files into chunks
2021-09-25 12:19:18 +12:00
if split {
2021-08-08 06:45:13 +12:00
var splitted []string
2021-09-25 12:19:18 +12:00
popupStatus = s("Splitting file...")
stat, _ := os.Stat(outputFile)
2021-05-30 07:23:22 +12:00
size := stat.Size()
finished := 0
2021-09-25 12:19:18 +12:00
chunkSize, _ := strconv.Atoi(splitSize)
2021-08-08 06:45:13 +12:00
// User can choose KiB, MiB, and GiB
2021-09-25 12:19:18 +12:00
if splitSelected == 0 {
2021-05-30 07:23:22 +12:00
chunkSize *= 1024
2021-09-25 12:19:18 +12:00
} else if splitSelected == 1 {
2021-05-30 07:23:22 +12:00
chunkSize *= 1048576
2021-09-25 12:19:18 +12:00
} else {
2021-05-30 07:23:22 +12:00
chunkSize *= 1073741824
}
2021-09-25 12:19:18 +12:00
chunks := int(math.Ceil(float64(size) / float64(chunkSize)))
fin, _ := os.Open(outputFile)
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
for i := 0; i < chunks; i++ {
fout, _ := os.Create(fmt.Sprintf("%s.%d", outputFile, i))
2021-05-30 07:23:22 +12:00
done := 0
2021-09-25 12:19:18 +12:00
for {
data := make([]byte, 1048576)
read, err := fin.Read(data)
if err != nil {
2021-05-30 07:23:22 +12:00
break
}
2021-09-25 12:19:18 +12:00
if !working {
2021-05-30 07:23:22 +12:00
fin.Close()
fout.Close()
2021-09-25 12:19:18 +12:00
mainStatus = "Operation cancelled by user."
mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
2021-08-08 06:45:13 +12:00
// If user cancels, remove the unfinished files
2021-09-25 12:19:18 +12:00
for _, j := range splitted {
2021-05-30 07:23:22 +12:00
os.Remove(j)
}
2021-09-25 12:19:18 +12:00
os.Remove(fmt.Sprintf("%s.%d", outputFile, i))
2021-05-30 07:23:22 +12:00
os.Remove(outputFile)
return
}
data = data[:read]
fout.Write(data)
done += read
2021-09-25 12:19:18 +12:00
if done >= chunkSize {
2021-05-30 07:23:22 +12:00
break
}
2021-09-25 12:19:18 +12:00
}
2021-05-30 07:23:22 +12:00
fout.Close()
finished++
2021-09-25 12:19:18 +12:00
splitted = append(splitted, fmt.Sprintf("%s.%d", outputFile, i))
progress = float32(finished) / float32(chunks)
progressInfo = fmt.Sprintf("%d/%d", finished, chunks)
2021-08-08 06:45:13 +12:00
giu.Update()
2021-05-30 07:23:22 +12:00
}
fin.Close()
2021-09-25 12:19:18 +12:00
if shredTemp {
2021-05-30 07:23:22 +12:00
progressInfo = ""
2021-09-25 12:19:18 +12:00
popupStatus = s("Shredding temporary files...")
shred([]string{inputFile + ".pcv"}, false)
2021-09-25 12:19:18 +12:00
} else {
os.Remove(inputFile + ".pcv")
2021-05-30 07:23:22 +12:00
}
}
2021-08-08 06:45:13 +12:00
// Remove the temporary file used to combine a splitted Picocrypt volume
2021-09-25 12:19:18 +12:00
if recombine {
2021-05-30 07:23:22 +12:00
os.Remove(inputFile)
}
2021-08-08 06:45:13 +12:00
// Delete the temporary zip file if user wishes
2021-09-25 12:19:18 +12:00
if len(allFiles) > 1 || len(onlyFolders) > 0 {
if shredTemp {
2021-05-30 07:23:22 +12:00
progressInfo = ""
2021-09-25 12:19:18 +12:00
popupStatus = s("Shredding temporary files...")
2021-08-08 06:45:13 +12:00
giu.Update()
shred([]string{inputFile}, false)
2021-09-25 12:19:18 +12:00
} else {
os.Remove(inputFile)
2021-05-30 07:23:22 +12:00
}
}
2021-09-25 12:19:18 +12:00
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++
2021-09-05 10:40:55 +12:00
}
2021-09-25 12:19:18 +12:00
} else {
os.Remove(inputFile)
}
} else {
for _, i := range onlyFiles {
os.Remove(i)
}
for _, i := range onlyFolders {
os.RemoveAll(i)
2021-09-05 10:40:55 +12:00
}
}
}
2021-05-30 07:23:22 +12:00
resetUI()
2021-08-08 06:45:13 +12:00
// If user chose to keep a corrupted/modified file, let them know
2021-09-25 12:19:18 +12:00
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}
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// Clear UI state
2021-05-30 07:23:22 +12:00
working = false
kept = false
key = nil
2021-09-25 12:19:18 +12:00
popupStatus = s("Ready.")
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// This function is run if an issue occurs during decryption
2021-09-25 12:19:18 +12:00
func broken() {
mainStatus = "The input file is either corrupted or intentionally modified."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
if recombine {
2021-08-08 06:45:13 +12:00
os.Remove(inputFile)
}
os.Remove(outputFile)
giu.Update()
}
2021-09-25 12:19:18 +12:00
// Generate file checksums
func generateChecksums(file string) {
fin, _ := os.Open(file)
2021-08-08 06:45:13 +12:00
// Clear UI state
2021-09-25 12:19:18 +12:00
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...")
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha1Selected {
csSha1 = s("Calculating...")
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha256Selected {
csSha256 = s("Calculating...")
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha3Selected {
csSha3 = s("Calculating...")
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if blake2bSelected {
csBlake2b = s("Calculating...")
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if blake2sSelected {
csBlake2s = s("Calculating...")
2021-08-08 06:45:13 +12:00
}
// Create the checksum objects
2021-09-25 12:19:18 +12:00
crcMd5 := md5.New()
crcSha1 := sha1.New()
crcSha256 := sha256.New()
crcSha3 := sha3.New256()
crcBlake2b, _ := blake2b.New256(nil)
crcBlake2s, _ := blake2s.New256(nil)
stat, _ := os.Stat(file)
2021-08-08 06:45:13 +12:00
total := stat.Size()
var done int64 = 0
2021-09-25 12:19:18 +12:00
for {
2021-08-08 06:45:13 +12:00
var data []byte
2021-09-25 12:19:18 +12:00
_data := make([]byte, 1048576)
size, err := fin.Read(_data)
if err != nil {
2021-08-08 06:45:13 +12:00
break
}
data = _data[:size]
2021-09-25 12:19:18 +12:00
if md5Selected {
crcMd5.Write(data)
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha1Selected {
crcSha1.Write(data)
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha256Selected {
crcSha256.Write(data)
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha3Selected {
crcSha3.Write(data)
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if blake2bSelected {
crcBlake2b.Write(data)
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if blake2sSelected {
crcBlake2s.Write(data)
2021-08-08 06:45:13 +12:00
}
done += int64(size)
2021-09-25 12:19:18 +12:00
csProgress = float32(done) / float32(total)
2021-08-08 06:45:13 +12:00
giu.Update()
}
2021-09-25 12:19:18 +12:00
csProgress = 0
if md5Selected {
csMd5 = hex.EncodeToString(crcMd5.Sum(nil))
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha1Selected {
csSha1 = hex.EncodeToString(crcSha1.Sum(nil))
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha256Selected {
csSha256 = hex.EncodeToString(crcSha256.Sum(nil))
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if sha3Selected {
csSha3 = hex.EncodeToString(crcSha3.Sum(nil))
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if blake2bSelected {
csBlake2b = hex.EncodeToString(crcBlake2b.Sum(nil))
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
if blake2sSelected {
csBlake2s = hex.EncodeToString(crcBlake2s.Sum(nil))
2021-08-08 06:45:13 +12:00
}
fin.Close()
giu.Update()
}
// Recursively shred all file(s) and folder(s) passed in as 'names'
func shred(names []string, separate bool) {
2021-08-10 11:51:25 +12:00
stopShredding = false
2021-05-30 07:23:22 +12:00
shredTotal = 0
shredDone = 0
2021-08-08 06:45:13 +12:00
// 'separate' is true if this function is being called from the encryption/decryption tab
2021-09-25 12:19:18 +12:00
if separate {
2021-09-05 10:40:55 +12:00
shredOverlay = s("Shredding...")
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// Walk through directories to get the total number of files for statistics
2021-09-25 12:19:18 +12:00
for _, name := range names {
filepath.Walk(name, func(path string, _ os.FileInfo, err error) error {
if err != nil {
2021-05-30 07:23:22 +12:00
return nil
}
2021-09-25 12:19:18 +12:00
stat, _ := os.Stat(path)
if !stat.IsDir() {
2021-05-30 07:23:22 +12:00
shredTotal++
}
return nil
})
}
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
for _, name := range names {
2021-05-30 07:23:22 +12:00
shredding = name
// Linux and macOS need a command with similar syntax and usage, so they're combined
if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
stat, _ := os.Stat(name)
if stat.IsDir() {
var coming []string
// Walk the folder recursively
filepath.Walk(name, func(path string, _ os.FileInfo, err error) error {
if err != nil {
return nil
}
if stopShredding {
return nil
}
stat, _ := os.Stat(path)
if !stat.IsDir() {
if len(coming) == 128 {
// Use a WaitGroup to parallelize shredding
var wg sync.WaitGroup
for i, j := range coming {
wg.Add(1)
go func(wg *sync.WaitGroup, id int, j string) {
defer wg.Done()
shredding = j
var cmd *exec.Cmd
if runtime.GOOS == "linux" {
cmd = exec.Command("shred", "-ufvz", "-n", strconv.Itoa(int(shredPasses)), j)
} else {
cmd = exec.Command("rm", "-rfP", j)
}
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Run()
shredDone++
shredUpdate(separate)
giu.Update()
}(&wg, i, j)
}
wg.Wait()
coming = nil
} else {
coming = append(coming, path)
}
}
return nil
})
for _, i := range coming {
if stopShredding {
break
}
go func(i string) {
shredding = i
var cmd *exec.Cmd
if runtime.GOOS == "linux" {
cmd = exec.Command("shred", "-ufvz", "-n", strconv.Itoa(int(shredPasses)), i)
} else {
cmd = exec.Command("rm", "-rfP", i)
}
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Run()
shredDone++
shredUpdate(separate)
giu.Update()
}(i)
}
if !stopShredding {
os.RemoveAll(name)
}
} else { // The path is a file, not a directory, so just shred it
shredding = name
var cmd *exec.Cmd
if runtime.GOOS == "linux" {
cmd = exec.Command("shred", "-ufvz", "-n", strconv.Itoa(int(shredPasses)), name)
} else {
cmd = exec.Command("rm", "-rfP", name)
}
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Run()
shredDone++
shredUpdate(separate)
}
} else if runtime.GOOS == "windows" {
stat, _ := os.Stat(name)
if stat.IsDir() {
// Walk the folder recursively
filepath.Walk(name, func(path string, _ os.FileInfo, err error) error {
if err != nil {
return nil
}
stat, _ := os.Stat(path)
if stat.IsDir() {
if stopShredding {
return nil
}
t := 0
files, _ := ioutil.ReadDir(path)
for _, f := range files {
if !f.IsDir() {
t++
}
}
shredDone += float32(t)
shredUpdate(separate)
shredding = strings.ReplaceAll(path, "\\", "/") + "/*"
cmd := exec.Command(sdelete64path, "*", "-p", strconv.Itoa(int(shredPasses)))
cmd.Dir = path
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Run()
giu.Update()
}
return nil
})
if !stopShredding {
// sdelete64 doesn't delete the empty folder, so I'll do it manually
os.RemoveAll(name)
}
} else {
shredding = name
cmd := exec.Command(sdelete64path, name, "-p", strconv.Itoa(int(shredPasses)))
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Run()
shredDone++
shredUpdate(separate)
}
}
2021-08-08 06:45:13 +12:00
giu.Update()
2021-09-25 12:19:18 +12:00
if stopShredding {
2021-08-10 11:51:25 +12:00
return
}
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// Clear UI state
2021-09-05 10:40:55 +12:00
shredding = s("Completed.")
2021-05-30 07:23:22 +12:00
shredProgress = 0
shredOverlay = ""
}
2021-08-08 06:45:13 +12:00
// Update shredding statistics
2021-09-25 12:19:18 +12:00
func shredUpdate(separate bool) {
if separate {
shredOverlay = fmt.Sprintf("%d/%d", int(shredDone), int(shredTotal))
shredProgress = shredDone / shredTotal
} else {
popupStatus = fmt.Sprintf("%d/%d", int(shredDone), int(shredTotal))
progress = shredDone / shredTotal
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
giu.Update()
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// Reset the UI to a clean state with nothing selected or checked
2021-09-25 12:19:18 +12:00
func resetUI() {
2021-08-08 06:45:13 +12:00
mode = ""
2021-09-25 12:19:18 +12:00
onlyFiles = nil
onlyFolders = nil
allFiles = nil
inputLabel = s("Drop files and folders into this window.")
2021-05-30 07:23:22 +12:00
password = ""
cPassword = ""
2021-09-05 10:40:55 +12:00
keyfiles = nil
2021-05-30 07:23:22 +12:00
keyfile = false
2021-09-05 10:40:55 +12:00
keyfileOrderMatters = false
2021-09-25 12:19:18 +12:00
keyfilePrompt = s("None selected.")
2021-05-30 07:23:22 +12:00
metadata = ""
2021-09-25 12:19:18 +12:00
metadataPrompt = "Metadata:"
metadataDisabled = false
2021-08-08 06:45:13 +12:00
shredTemp = false
2021-05-30 07:23:22 +12:00
keep = false
reedsolo = false
split = false
splitSize = ""
2021-09-25 12:19:18 +12:00
splitSelected = 1
2021-05-30 07:23:22 +12:00
fast = false
2021-09-25 12:19:18 +12:00
deleteWhenDone = false
2021-08-08 06:45:13 +12:00
paranoid = false
2021-09-25 12:19:18 +12:00
compress = false
encryptFilename = false
inputFile = ""
outputFile = ""
2021-05-30 07:23:22 +12:00
progress = 0
progressInfo = ""
2021-09-25 12:19:18 +12:00
mainStatus = "Ready."
mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
2021-08-08 06:45:13 +12:00
giu.Update()
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// Reed-Solomon encoder
2021-09-25 12:19:18 +12:00
func rsEncode(rs *infectious.FEC, data []byte) []byte {
2021-08-08 06:45:13 +12:00
var res []byte
2021-09-25 12:19:18 +12:00
rs.Encode(data, func(s infectious.Share) {
res = append(res, s.DeepCopy().Data[0])
2021-08-08 06:45:13 +12:00
})
return res
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// Reed-Solomon decoder
2021-09-25 12:19:18 +12:00
func rsDecode(rs *infectious.FEC, data []byte) ([]byte, error) {
tmp := make([]infectious.Share, rs.Total())
for i := 0; i < rs.Total(); i++ {
2021-08-08 06:45:13 +12:00
tmp[i] = infectious.Share{
2021-09-25 12:19:18 +12:00
Number: i,
Data: []byte{data[i]},
2021-08-08 06:45:13 +12:00
}
2021-05-30 07:23:22 +12:00
}
2021-09-25 12:19:18 +12:00
res, err := rs.Decode(nil, tmp)
if err != nil {
if rs.Total() == 136 {
return data[:128], err
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
return data[:rs.Total()/3], err
2021-08-08 06:45:13 +12:00
}
2021-09-25 12:19:18 +12:00
return res, nil
2021-05-30 07:23:22 +12:00
}
2021-08-08 06:45:13 +12:00
// PKCS7 Pad (for use with Reed-Solomon, not for cryptographic purposes)
2021-09-25 12:19:18 +12:00
func pad(data []byte) []byte {
padLen := 128 - len(data)%128
padding := bytes.Repeat([]byte{byte(padLen)}, padLen)
return append(data, padding...)
2021-08-08 06:45:13 +12:00
}
// PKCS7 Unpad
2021-09-25 12:19:18 +12:00
func unpad(data []byte) []byte {
2021-08-08 06:45:13 +12:00
length := len(data)
padLen := int(data[length-1])
return data[:length-padLen]
}
2021-09-25 12:19:18 +12:00
func genPassword() string {
2021-08-11 15:35:35 +12:00
chars := ""
2021-09-25 12:19:18 +12:00
if genpassUpper {
2021-08-11 15:35:35 +12:00
chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
}
2021-09-25 12:19:18 +12:00
if genpassLower {
2021-08-11 15:35:35 +12:00
chars += "abcdefghijklmnopqrstuvwxyz"
}
2021-09-25 12:19:18 +12:00
if genpassNums {
2021-08-11 15:35:35 +12:00
chars += "1234567890"
}
2021-09-25 12:19:18 +12:00
if genpassSymbols {
2021-08-11 15:35:35 +12:00
chars += "-=!@#$^&()_+?"
}
2021-09-25 12:19:18 +12:00
if chars == "" {
2021-08-11 15:35:35 +12:00
return chars
}
2021-09-25 12:19:18 +12:00
tmp := make([]byte, genpassLength)
for i := 0; i < int(genpassLength); i++ {
j, _ := rand.Int(rand.Reader, new(big.Int).SetUint64(uint64(len(chars))))
2021-08-10 11:51:25 +12:00
tmp[i] = chars[j.Int64()]
}
2021-09-25 12:19:18 +12:00
if genpassCopy {
2021-08-11 15:35:35 +12:00
clipboard.WriteAll(string(tmp))
}
2021-08-10 11:51:25 +12:00
return string(tmp)
}
2021-09-25 12:19:18 +12:00
// 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 {
2021-09-05 10:40:55 +12:00
return i.data[k]
}
}
}
}
2021-09-25 12:19:18 +12:00
2021-09-05 10:40:55 +12:00
}
}
return term
}
2021-09-25 12:19:18 +12:00
func main() {
2021-09-05 10:40:55 +12:00
// Parse locales
var obj map[string]json.RawMessage
2021-09-25 12:19:18 +12:00
json.Unmarshal(localeBytes, &obj)
for i := range obj {
2021-09-05 10:40:55 +12:00
var tmp []string
2021-09-25 12:19:18 +12:00
json.Unmarshal(obj[i], &tmp)
locales = append(locales, locale{
iso: i,
data: tmp,
2021-09-05 10:40:55 +12:00
})
}
// Check system locale
2021-09-25 12:19:18 +12:00
for _, i := range locales {
tmp, err := jibber_jabber.DetectIETF()
if err == nil {
if strings.HasPrefix(tmp, i.iso) {
2021-09-05 10:40:55 +12:00
selectedLocale = i.iso
2021-09-25 12:19:18 +12:00
for j, k := range allLocales {
if k == i.iso {
2021-09-05 10:40:55 +12:00
languageSelected = int32(j)
}
}
}
}
}
// Create a temporary file to store sdelete64.exe
sdelete64, _ := os.CreateTemp("", "sdelete64.*.exe")
sdelete64path = sdelete64.Name()
sdelete64.Write(sdelete64bytes)
sdelete64.Close()
cmd := exec.Command(sdelete64path, "/accepteula")
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
cmd.Run()
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
// Set a universal font
giu.SetDefaultFontFromBytes(font, 18)
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
// Create the master window
window := giu.NewMasterWindow("Picocrypt", 442, 532, giu.MasterWindowFlagsNotResizable)
2021-08-08 06:45:13 +12:00
dialog.Init()
2021-09-25 12:19:18 +12:00
// Set window icon
reader := bytes.NewReader(icon)
decoded, _ := png.Decode(reader)
window.SetIcon([]image.Image{decoded})
2021-08-08 06:45:13 +12:00
2021-09-25 12:19:18 +12:00
// Set callbacks
2021-05-30 07:23:22 +12:00
window.SetDropCallback(onDrop)
2021-09-25 12:19:18 +12:00
window.SetCloseCallback(func() bool {
return !working
2021-08-08 06:45:13 +12:00
})
2021-09-25 12:19:18 +12:00
// Set universal DPI
2021-08-08 06:45:13 +12:00
dpi = giu.Context.GetPlatform().GetContentScale()
2021-09-25 12:19:18 +12:00
// 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{0, 255, 0, 255}
}
}
}
}()
// Start the UI
window.Run(draw)
// Window closed, clean up
os.Remove(sdelete64path)
2021-05-30 07:23:22 +12:00
}