Picocrypt/src/unstable/Picocrypt.go

1633 lines
38 KiB
Go

package main
/*
Picocrypt v1.13
Copyright (c) Evan Su (https://evansu.cc)
Released under a GNU GPL v3 License
https://github.com/HACKERALERT/Picocrypt
~ In cryptography we trust ~
*/
import (
"io"
"os"
"fmt"
"math"
"time"
"sync"
"os/exec"
"strings"
"strconv"
"runtime"
"io/ioutil"
"image/color"
"crypto/md5"
"archive/zip"
"crypto/rand"
"crypto/sha1"
"encoding/hex"
"path/filepath"
"crypto/sha256"
"runtime/debug"
"github.com/pkg/browser"
"github.com/zeebo/blake3"
"golang.org/x/crypto/sha3"
"golang.org/x/crypto/argon2"
g "github.com/AllenDang/giu"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s"
"github.com/atotto/clipboard"
di "github.com/OpenDiablo2/dialog"
"github.com/klauspost/reedsolomon"
ig "github.com/AllenDang/imgui-go"
"golang.org/x/crypto/chacha20poly1305"
"github.com/HACKERALERT/Picocypher/monocypher"
)
var version = "v1.13"
var exe,_ = os.Executable()
var rootDir = filepath.Dir(exe)
// Global variables
var dpi float32
var mode string
var working = false
var onlyFiles []string
var onlyFolders []string
var allFiles []string
var inputFile string
var outputFile string
// UI-related global variables
var tab = 0
var inputLabel = "Drag and drop file(s) and folder(s) into this window."
var outputEntry string
var outputWidth float32 = 376
var orLabel = "or"
var passwordState = g.InputTextFlags_Password
var showPassword = false
var keyfile = false
var keyfilePrompt = "Keyfile (optional):"
var progress float32 = 0
var progressInfo = ""
var status = "Ready."
var _status = "adfs"
var _status_color = color.RGBA{0xff,0xff,0xff,255}
var splitUnits = []string{
"KB",
"MB",
"GB",
}
var splitSelected int32
var items = []string{
"Normal (4 passes)",
"Paranoid (Still in development, don't use)",
}
var itemSelected int32
var shredProgress float32
var shredDone float32
var shredTotal float32
var shredOverlay string
var shredding = "Ready."
var recombine bool
// User input variables
var password string
var cPassword string
var keyfilePath string
var keyfileLabel = "Use a keyfile"
var metadata string
var keep bool
var erase bool
var reedsolo bool
var split bool
var splitSize string
var fast bool
var _fast bool
var kept = false
// Reed-Solomon encoders
var rs5_128,_ = reedsolomon.New(5,128)
var rs10_128,_ = reedsolomon.New(10,128)
var rs16_128,_ = reedsolomon.New(16,128)
var rs24_128,_ = reedsolomon.New(24,128)
var rs32_128,_ = reedsolomon.New(32,128)
var rs64_128,_ = reedsolomon.New(64,128)
// File checksum generator variables
var cs_md5 string
var cs_sha1 string
var cs_sha256 string
var cs_sha3_256 string
var cs_blake2b string
var cs_blake2s string
var cs_blake3 string
var cs_validate string
var md5_color = color.RGBA{0x10,0x10,0x10,255}
var sha1_color = color.RGBA{0x10,0x10,0x10,255}
var sha256_color = color.RGBA{0x10,0x10,0x10,255}
var sha3_256_color = color.RGBA{0x10,0x10,0x10,255}
var blake2b_color = color.RGBA{0x10,0x10,0x10,255}
var blake2s_color = color.RGBA{0x10,0x10,0x10,255}
var blake3_color = color.RGBA{0x10,0x10,0x10,255}
var cs_progress float32 = 0
var md5_selected = false
var sha1_selected = false
var sha256_selected = false
var sha3_256_selected = false
var blake2b_selected = false
var blake2s_selected = false
var blake3_selected = false
// Create the user interface
func startUI(){
g.SingleWindow("Picocrypt").Layout(
g.Style().SetColor(ig.StyleColorBorder,color.RGBA{0x10,0x10,0x10,255}).To(
// The tab bar, which contains different tabs for different features
g.TabBar("TabBar").Layout(
// File encryption/decryption tab
g.TabItem("Encryption/decryption").Layout(
// Update 'tab' to indicate active tab
g.Custom(func(){
if g.IsItemActive(){
tab = 0
}
}),
// Confirm overwrite with a modal
g.PopupModal("Confirmation").Layout(
g.Label("Output already exists. Overwrite?"),
g.Row(
g.Button("No").Size(110,0).OnClick(func(){
g.CloseCurrentPopup()
}),
g.Dummy(-118,0),
g.Button("Yes").Size(110,0).OnClick(func(){
g.CloseCurrentPopup()
go work()
}),
),
),
// Label listing the input files and a button to clear input files
//g.Dummy(0,10),
g.Row(
g.Label(inputLabel),
g.Dummy(-55,0),
g.Button("Clear").Size(46,0).OnClick(resetUI),
),
// Allow user to choose a custom output path and name
//g.Dummy(10,0),
g.Label("Save output as:"),
g.Row(
g.InputText("##output",&outputEntry).Size(outputWidth/dpi),
g.Label(orLabel),
g.Button("Save as").OnClick(func(){
file,_ := di.File().Title("Save as").Save()
// Return if user canceled the file dialog
if file==""{
return
}
// Remove the extra ".pcv" extension if needed
if strings.HasSuffix(file,".pcv"){
file = file[:len(file)-4]
}
outputEntry = file
}),
),
// Prompt for password
//g.Dummy(10,0),
g.Row(
g.Label("Password:"),
g.Dummy(-220,0),
g.Label(keyfilePrompt),
),
g.Row(
g.InputText("##password",&password).Size(200/dpi).Flags(passwordState),
g.Checkbox("##showPassword",&showPassword).OnChange(func(){
if passwordState==g.InputTextFlags_Password{
passwordState = g.InputTextFlags_None
}else{
passwordState = g.InputTextFlags_Password
}
g.Update()
}),
g.Dummy(-220,0),
g.Checkbox(keyfileLabel,&keyfile).OnChange(func(){
if !keyfile{
keyfileLabel = "Use a keyfile"
return
}
filename,err := di.File().Load()
if err!=nil{
keyfile = false
return
}
keyfileLabel = filename
keyfilePath = filename
}),
),
// Prompt to confirm password
//g.Dummy(10,0),
g.Label("Confirm password:"),
g.InputText("##cPassword",&cPassword).Size(200/dpi).Flags(passwordState),
// Optional metadata
//g.Dummy(10,0),
g.Label("Metadata (optional):"),
g.InputTextMultiline("##metadata",&metadata).Size(230,126),
// Advanced options can be enabled with checkboxes
//g.Dummy(10,0),
g.Checkbox("Keep decrypted output even if it's corrupted or modified",&keep),
g.Checkbox("Securely shred the original file(s) and folder(s)",&erase),
g.Row(
g.Checkbox("(Not finished yet) Encode with Reed-Solomon to prevent corruption",&reedsolo),
g.Button("?").OnClick(func(){
browser.OpenURL("https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction")
}),
),
g.Row(
g.Checkbox("Split output into chunks of",&split),
g.InputText("##splitSize",&splitSize).Size(30).Flags(g.InputTextFlags_CharsDecimal),
g.Combo("##splitter",splitUnits[splitSelected],splitUnits,&splitSelected).Size(40),
//g.Label("MB"),
),
g.Checkbox("Fast mode (less secure, not as durable)",&fast),
// Start and cancel buttons
//g.Dummy(10,0),
g.Button("Start").Size(-1,20).OnClick(func(){
if password!=cPassword{
_status = "Passwords don't match."
_status_color = color.RGBA{0xff,0x00,0x00,255}
return
}
if mode=="encrypt"{
outputFile = outputEntry+".pcv"
}else{
outputFile = outputEntry
}
_,err := os.Stat(outputFile)
if err==nil{
g.OpenPopup("Confirmation")
}else{
go work()
}
}),
//g.Dummy(10,0),
g.Style().SetColor(ig.StyleColorText,_status_color).To(
g.Label(_status),
),
),
// File shredder tab
g.TabItem("Shredder").Layout(
// Update 'tab' to indicate active tab
g.Custom(func(){
if g.IsItemActive(){
tab = 1
}
}),
g.Label("Select a mode below and drop file(s) and folder(s) here."),
g.Label("Warning: Anything dropped here will be shredded immediately!"),
//g.Dummy(10,0),
g.Combo("##shredder_mode",items[itemSelected],items,&itemSelected).Size(463),
//g.Dummy(10,0),
g.ProgressBar(shredProgress).Overlay(shredOverlay).Size(-1,0),
g.Label(shredding).Wrapped(true),
),
// File checksum generator tab
g.TabItem("Checksum").Layout(
// Update 'tab' to indicate active tab
g.Custom(func(){
if g.IsItemActive(){
tab = 2
}
}),
g.Label("Toggle the hashes you would like to generate and drop a file here."),
// MD5
//g.Dummy(10,0),
g.Row(
g.Checkbox("MD5:",&md5_selected),
g.Dummy(-45,0),
g.Button("Copy##md5").Size(36,0).OnClick(func(){
clipboard.WriteAll(cs_md5)
}),
),
g.Style().SetColor(ig.StyleColorBorder,md5_color).To(
g.InputText("##cs_md5",&cs_md5).Size(-1).Flags(g.InputTextFlags_ReadOnly),
),
// SHA1
//g.Dummy(10,0),
g.Row(
g.Checkbox("SHA1:",&sha1_selected),
g.Dummy(-45,0),
g.Button("Copy##sha1").Size(36,0).OnClick(func(){
clipboard.WriteAll(cs_sha1)
}),
),
g.Style().SetColor(ig.StyleColorBorder,sha1_color).To(
g.InputText("##cs_sha1",&cs_sha1).Size(-1).Flags(g.InputTextFlags_ReadOnly),
),
// SHA256
//g.Dummy(10,0),
g.Row(
g.Checkbox("SHA256:",&sha256_selected),
g.Dummy(-45,0),
g.Button("Copy##sha256").Size(36,0).OnClick(func(){
clipboard.WriteAll(cs_sha256)
}),
),
g.Style().SetColor(ig.StyleColorBorder,sha256_color).To(
g.InputText("##cs_sha256",&cs_sha256).Size(-1).Flags(g.InputTextFlags_ReadOnly),
),
// SHA3-256
//g.Dummy(10,0),
g.Row(
g.Checkbox("SHA3-256:",&sha3_256_selected),
g.Dummy(-45,0),
g.Button("Copy##sha3_256").Size(36,0).OnClick(func(){
clipboard.WriteAll(cs_sha3_256)
}),
),
g.Style().SetColor(ig.StyleColorBorder,sha3_256_color).To(
g.InputText("##cs_sha3_256",&cs_sha3_256).Size(-1).Flags(g.InputTextFlags_ReadOnly),
),
// BLAKE2b
//g.Dummy(10,0),
g.Row(
g.Checkbox("BLAKE2b:",&blake2b_selected),
g.Dummy(-45,0),
g.Button("Copy##blake2b").Size(36,0).OnClick(func(){
clipboard.WriteAll(cs_blake2b)
}),
),
g.Style().SetColor(ig.StyleColorBorder,blake2b_color).To(
g.InputText("##cs_blake2b",&cs_blake2b).Size(-1).Flags(g.InputTextFlags_ReadOnly),
),
// BLAKE2s
//g.Dummy(10,0),
g.Row(
g.Checkbox("BLAKE2s:",&blake2s_selected),
g.Dummy(-45,0),
g.Button("Copy##blake2s").Size(36,0).OnClick(func(){
clipboard.WriteAll(cs_blake2s)
}),
),
g.Style().SetColor(ig.StyleColorBorder,blake2s_color).To(
g.InputText("##cs_blake2s",&cs_blake2s).Size(-1).Flags(g.InputTextFlags_ReadOnly),
),
// BLAKE3
//g.Dummy(10,0),
g.Row(
g.Checkbox("BLAKE3:",&blake3_selected),
g.Dummy(-45,0),
g.Button("Copy##blake3").Size(36,0).OnClick(func(){
clipboard.WriteAll(cs_blake3)
}),
),
g.Style().SetColor(ig.StyleColorBorder,blake3_color).To(
g.InputText("##cs_blake3",&cs_blake3).Size(-1).Flags(g.InputTextFlags_ReadOnly),
),
// Input box for validating checksum
//g.Dummy(10,0),
g.Label("Validate a checksum:"),
g.InputText("##cs_validate",&cs_validate).Size(-1).OnChange(func(){
md5_color = color.RGBA{0x10,0x10,0x10,255}
sha1_color = color.RGBA{0x10,0x10,0x10,255}
sha256_color = color.RGBA{0x10,0x10,0x10,255}
sha3_256_color = color.RGBA{0x10,0x10,0x10,255}
blake2b_color = color.RGBA{0x10,0x10,0x10,255}
blake2s_color = color.RGBA{0x10,0x10,0x10,255}
blake3_color = color.RGBA{0x10,0x10,0x10,255}
if cs_validate==""{
return
}
if cs_validate==cs_md5{
md5_color = color.RGBA{0x00,0xff,0x00,255}
}else if cs_validate==cs_sha1{
sha1_color = color.RGBA{0x00,0xff,0x00,255}
}else if cs_validate==cs_sha256{
sha256_color = color.RGBA{0x00,0xff,0x00,255}
}else if cs_validate==cs_sha3_256{
sha3_256_color = color.RGBA{0x00,0xff,0x00,255}
}else if cs_validate==cs_blake2b{
blake2b_color = color.RGBA{0x00,0xff,0x00,255}
}else if cs_validate==cs_blake2s{
blake2s_color = color.RGBA{0x00,0xff,0x00,255}
}else if cs_validate==cs_blake3{
blake3_color = color.RGBA{0x00,0xff,0x00,255}
}
g.Update()
}),
// Progress bar
//g.Dummy(10,0),
g.Label("Progress:"),
g.ProgressBar(cs_progress).Size(-1,0),
),
g.TabItem("About").Layout(
// Update 'tab' to indicate active tab
g.Custom(func(){
if g.IsItemActive(){
tab = 3
}
}),
//g.Dummy(30,0),
g.Label("Picocrypt "+version+", created by Evan Su (https://evansu.cc)"),
),
),
),
)
if working{
g.SingleWindow("Working..").IsOpen(&working).Layout(
//g.Dummy(30,0),
g.Label("Tips:"),
g.Label(" - Choose a strong password with more than 16 characters."),
g.Label(" - Use a unique password that isn't used anywhere else."),
g.Label(" - Trust no one but yourself and never give out your key."),
g.Label(" - For highly sensitive files, encrypt them while offline."),
g.Label(" - An antivirus can be beneficial to prevent keyloggers."),
g.Label(" - Encrypt your root filesystem for maximal security."),
g.Dummy(0,-50),
// Progress bar
g.Row(
g.ProgressBar(progress).Size(-54,0).Overlay(progressInfo),
g.Dummy(-59,0),
g.Button("Cancel").Size(50,0).OnClick(func(){
working = false
}),
),
//g.Dummy(10,0),
g.Label(status),
)
}
}
// Handle files dropped into Picocrypt by user
func onDrop(names []string){
_status = ""
recombine = false
if tab==0{
// Clear variables
onlyFiles = nil
onlyFolders = nil
allFiles = nil
files,folders := 0,0
// Reset UI
resetUI()
// Hide the ".pcv" label
orLabel = "or"
outputWidth = 376
// There's only one dropped item
if len(names)==1{
stat,_ := os.Stat(names[0])
// Check if dropped item is a file or a folder
if stat.IsDir(){
folders++
inputLabel = "1 folder selected."
// Add the folder
onlyFolders = append(onlyFolders,names[0])
// Set 'outputEntry' to 'Encrypted.zip' in the same directory
outputEntry = filepath.Join(filepath.Dir(names[0]),"Encrypted.zip")
mode = "encrypt"
// Show the ".pcv" file extension
orLabel = ".pcv or"
outputWidth = 341
}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{
mode = "decrypt"
inputLabel = name+" (will decrypt)"
if isSplit{
ind := strings.Index(names[0],".pcv")
names[0] = names[0][:ind]
outputEntry = names[0]
recombine = true
}else{
outputEntry = names[0][:len(names[0])-4]
}
// Open input file in read-only mode
fin,_ := os.Open(names[0])
// Read metadata and insert into box
fin.Read(make([]byte,133))
tmp := make([]byte,138)
fin.Read(tmp)
tmp = rsDecode(tmp,rs10_128,10)
metadataLength,_ := strconv.Atoi(string(tmp))
//fmt.Println(metadataLength)
tmp = make([]byte,metadataLength)
fin.Read(tmp)
metadata = string(tmp)
flags := make([]byte,133)
fin.Read(flags)
flags = rsDecode(flags,rs5_128,5)
if flags[1]==1{
keyfilePrompt = "Keyfile (required):"
keyfileLabel = "Click here to select keyfile."
}
fin.Close()
}else{
mode = "encrypt"
inputLabel = name+" (will encrypt)"
outputEntry = names[0]
// Show the ".pcv" file extension
orLabel = ".pcv or"
outputWidth = 341
}
// Add the file
onlyFiles = append(onlyFiles,names[0])
inputFile = names[0]
}
}else{
mode = "encrypt"
// Show the ".pcv" file extension
orLabel = ".pcv or"
outputWidth = 341
// 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{
// If folders==0, then there are multiple files
inputLabel = fmt.Sprintf("%d files selected.",files)
}else if files==0{
// If files==0, then there are multiple folders
inputLabel = fmt.Sprintf("%d folders selected.",folders)
}else{
// There are multiple files and folders
if files==1&&folders>1{
inputLabel = fmt.Sprintf("1 file and %d folders selected.",folders)
}else if folders==1&&files>1{
inputLabel = fmt.Sprintf("%d files and 1 folder selected.",files)
}else if folders==1&&files==1{
inputLabel = "1 file and 1 folder selected."
}else{
inputLabel = fmt.Sprintf("%d files and %d folders selected.",files,folders)
}
}
// Set 'outputEntry' to 'Encrypted.zip' in the same directory
outputEntry = filepath.Join(filepath.Dir(names[0]),"Encrypted.zip")
}
// If there are folders that were dropped, recusively add all files into '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
})
}
}
}else if tab==1{
go shred(names,true)
}else if tab==2{
go generateChecksums(names[0])
}
// Update the UI
g.Update()
}
// Start encryption/decryption
func work(){
debug.FreeOSMemory()
// Set some variables
working = true
//headerBroken := false
//reedsoloFixed := 0
//reedsoloErrors := 0
var salt []byte
var nonce []byte
var keyHash []byte
var _keyHash []byte
var khash []byte
var khash_hash []byte
var _khash_hash []byte
var nonces []byte
_fast = fast
// Set the output file based on mode
if mode=="encrypt"{
// Compress files into a zip archive
if len(allFiles)>1||len(onlyFolders)>0{
rootDir := filepath.Dir(outputEntry)
inputFile = outputEntry
fmt.Println(inputFile)
file,_ := os.Create(inputFile)
w := zip.NewWriter(file)
for _,path := range(allFiles){
if path==inputFile{
continue
}
stat,_ := os.Stat(path)
header,_ := zip.FileInfoHeader(stat)
header.Name = strings.Replace(path,rootDir,"",1)
header.Method = zip.Deflate
writer,_ := w.CreateHeader(header)
file,_ := os.Open(path)
io.Copy(writer,file)
file.Close()
}
w.Flush()
w.Close()
file.Close()
}
}
if recombine{
status = "Recombining file..."
total := 0
for{
_,err := os.Stat(fmt.Sprintf("%s.%d",inputFile+".pcv",total))
if err!=nil{
break
}
total++
}
fout,_ := os.OpenFile(
outputEntry+".pcv",
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
0755,
)
for i:=0;i<total;i++{
fin,_ := os.Open(fmt.Sprintf("%s.%d",inputFile+".pcv",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)
g.Update()
}
fout.Close()
outputFile = outputEntry
inputFile = outputEntry+".pcv"
progressInfo = ""
}
fmt.Println(inputFile)
stat,_ := os.Stat(inputFile)
total := stat.Size()
fmt.Println(total)
// Open input file in read-only mode
fin,_ := os.Open(inputFile)
var fout *os.File
fmt.Println(mode)
// If encrypting, generate values; If decrypting, read values from file
if mode=="encrypt"{
status = "Generating values..."
g.Update()
fout,_ = os.OpenFile(
outputFile,
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
0755,
)
// Argon2 salt and XChaCha20 nonce
salt = make([]byte,16)
nonce = make([]byte,24)
// Write version to file
fout.Write(rsEncode([]byte(version),rs5_128,133))
// Encode the length of the metadata with Reed-Solomon
metadataLength := []byte(fmt.Sprintf("%010d",len(metadata)))
//fmt.Println("metadataLength:",metadataLength)
metadataLength = rsEncode(metadataLength,rs10_128,138)
// Write the length of the metadata to file
fout.Write(metadataLength)
// Write the actual metadata
fout.Write([]byte(metadata))
flags := make([]byte,5)
if fast{
flags[0] = 1
}
if keyfile{
flags[1] = 1
}
//fmt.Println("flags:",flags)
flags = rsEncode(flags,rs5_128,133)
fout.Write(flags)
// Fill salt and nonce with Go's CSPRNG
rand.Read(salt)
rand.Read(nonce)
fmt.Println("salt: ",salt)
fmt.Println("nonce: ",nonce)
// Encode salt with Reed-Solomon and write to file
_salt := rsEncode(salt,rs16_128,144)
fout.Write(_salt)
// Encode nonce with Reed-Solomon and write to file
tmp := rsEncode(nonce,rs24_128,152)
fout.Write(tmp)
// Write placeholder for hash of key
fout.Write(make([]byte,192))
// Write placeholder for hash of hash of keyfile
fout.Write(make([]byte,160))
pairs := int(math.Ceil(float64(total)/1048576))
offset := 152*pairs+144
// Write placeholder for nonce/Poly1305 pairs
fout.Write(make([]byte,offset))
}else{
status = "Reading values..."
g.Update()
version := make([]byte,133)
fin.Read(version)
version = rsDecode(version,rs5_128,5)
tmp := make([]byte,138)
fin.Read(tmp)
tmp = rsDecode(tmp,rs10_128,10)
metadataLength,_ := strconv.Atoi(string(tmp))
//fmt.Println("metadataLength",metadataLength)
fin.Read(make([]byte,metadataLength))
flags := make([]byte,133)
fin.Read(flags)
flags = rsDecode(flags,rs5_128,5)
//fmt.Println("flags",flags)
fast = flags[0]==1
keyfile = flags[1]==1
salt = make([]byte,144)
fin.Read(salt)
salt = rsDecode(salt,rs16_128,16)
nonce = make([]byte,152)
fin.Read(nonce)
nonce = rsDecode(nonce,rs24_128,24)
fmt.Println("salt: ",salt)
fmt.Println("nonce: ",nonce)
_keyHash = make([]byte,192)
fin.Read(_keyHash)
_keyHash = rsDecode(_keyHash,rs64_128,64)
//fmt.Println("keyHash",keyHash)
_khash_hash = make([]byte,160)
fin.Read(_khash_hash)
_khash_hash = rsDecode(_khash_hash,rs32_128,32)
//fmt.Println("crcHash",crcHash)
_tmp := math.Ceil(float64(total-int64(metadataLength+1196))/float64(1048728))
nonces = make([]byte,int(_tmp*152)+144)
fin.Read(nonces)
//fmt.Println("Nonces: ",nonces)
}
g.Update()
status = "Deriving key..."
progress = 0
// Derive encryption/decryption key
var key []byte
if fast{
key = argon2.IDKey(
[]byte(password),
salt,
4,
131072,
4,
32,
)[:]
}else{
key = argon2.IDKey(
[]byte(password),
salt,
8,
1048576,
8,
32,
)[:]
}
fmt.Println("key",key)
if !working{
_status = "Operation cancelled by user."
_status_color = color.RGBA{0xff,0xff,0xff,255}
fast = _fast
debug.FreeOSMemory()
return
}
if keyfile{
kin,_ := os.Open(keyfilePath)
kstat,_ := os.Stat(keyfilePath)
fmt.Println(kstat.Size())
kbytes := make([]byte,kstat.Size())
kin.Read(kbytes)
kin.Close()
ksha3 := sha3.New256()
ksha3.Write(kbytes)
khash = ksha3.Sum(nil)
khash_sha3 := sha3.New256()
khash_sha3.Write(khash)
khash_hash = khash_sha3.Sum(nil)
fmt.Println("khash",khash)
fmt.Println("khash_hash",khash_hash)
}
//key = make([]byte,32)
fmt.Println("output",outputFile)
sha3_512 := sha3.New512()
sha3_512.Write(key)
keyHash = sha3_512.Sum(nil)
//fmt.Println("keyHash: ",keyHash)
// Check is password is correct
if mode=="decrypt"{
keyCorrect := true
keyfileCorrect := true
var tmp bool
for i,j := range(_keyHash){
if keyHash[i]!=j{
keyCorrect = false
break
}
}
if keyfile{
for i,j := range(_khash_hash){
if khash_hash[i]!=j{
keyfileCorrect = false
break
}
}
tmp = !keyCorrect||!keyfileCorrect
}else{
tmp = !keyCorrect
}
if tmp{
if keep{
kept = true
}else{
fin.Close()
working = false
if !keyCorrect{
_status = "The provided password is incorrect."
}else{
_status = "The provided keyfile is incorrect."
}
_status_color = color.RGBA{0xff,0x00,0x00,255}
key = nil
fast = _fast
if recombine{
os.Remove(inputFile)
}
debug.FreeOSMemory()
return
}
}
fout,_ = os.OpenFile(
outputFile,
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
0755,
)
}
if keyfile{
// XOR key and keyfile
tmp := key
key = make([]byte,32)
for i,_ := range(key){
key[i] = tmp[i]^khash[i]
}
fmt.Println("key",key)
}
done := 0
counter := 0
startTime := time.Now()
cipher,_ := chacha20poly1305.NewX(key)
if mode=="decrypt"{
_mac := nonces[len(nonces)-144:]
_mac = rsDecode(_mac,rs16_128,16)
//fmt.Println("_mac ",_mac)
nonces = nonces[:len(nonces)-144]
var tmp []byte
var chunk []byte
for i,j := range(nonces){
chunk = append(chunk,j)
if (i+1)%152==0{
chunk = rsDecode(chunk,rs24_128,24)
for _,k := range(chunk){
tmp = append(tmp,k)
}
chunk = nil
}
}
/*for _,j := range(_mac){
tmp = append(tmp,j)
}*/
var authentic bool
nonces,authentic = monocypher.Unlock(tmp,nonce,key,_mac)
if !authentic{
if keep{
kept = true
}else{
fin.Close()
fout.Close()
working = false
_status = "The file is either corrupted or intentionally modified."
_status_color = color.RGBA{0xff,0x00,0x00,255}
fast = _fast
debug.FreeOSMemory()
return
}
}
//fmt.Println("UNENCRYPTED NONCES: ",nonces)
}
for{
if !working{
fin.Close()
fout.Close()
os.Remove(outputFile)
_status = "Operation cancelled by user."
_status_color = color.RGBA{0xff,0xff,0xff,255}
fast = _fast
debug.FreeOSMemory()
return
}
//fmt.Println("Encrypt/decrypt loop")
var _data []byte
var data []byte
var _nonce []byte
if mode=="encrypt"{
_data = make([]byte,1048576)
}else{
_data = make([]byte,1048592)
}
size,err := fin.Read(_data)
if err!=nil{
break
}
data = _data[:size]
if mode=="encrypt"{
_nonce = make([]byte,24)
rand.Read(_nonce)
for _,i := range(_nonce){
nonces = append(nonces,i)
}
}else{
_nonce = nonces[counter*24:counter*24+24]
}
//fmt.Println("Data nonce: ",_nonce)
//fmt.Println("Data: ",data)
if mode=="encrypt"{
if fast{
data = cipher.Seal(nil,_nonce,data,nil)
fout.Write(data)
//crc.Write(data)
}else{
mac,data := monocypher.Lock(data,_nonce,key)
fout.Write(data)
fout.Write(mac)
//crc.Write(data)
//crc.Write(mac)
}
//fout.Write(data)
}else{
//fmt.Println("DECODE LOOP")
//crc.Write(data)
if fast{
data,err = cipher.Open(nil,_nonce,data,nil)
if err!=nil{
if keep{
kept = true
mac := data[len(data)-16:]
data = data[:len(data)-16]
data,_ = monocypher.Unlock(data,_nonce,key,mac)
}else{
fin.Close()
fout.Close()
broken()
fast = _fast
debug.FreeOSMemory()
return
}
}
}else{
//crc.Write(data)
mac := data[len(data)-16:]
data = data[:len(data)-16]
var authentic bool
data,authentic = monocypher.Unlock(data,_nonce,key,mac)
if !authentic{
if keep{
kept = true
}else{
fin.Close()
fout.Close()
broken()
fast = _fast
debug.FreeOSMemory()
return
}
}
}
fout.Write(data)
//fmt.Println(authentic)
//fmt.Println("DECRYPTED DATA: ",data)
}
done += 1048576
counter++
progress = float32(done)/float32(total)
elapsed:= float64(int64(time.Now().Sub(startTime)))/float64(1000000000)
speed := (float64(done)/elapsed)/1000000
eta := math.Abs(float64(total-int64(done))/(speed*1000000))
progressInfo = fmt.Sprintf("%.2f%%",progress*100)
status = fmt.Sprintf("Working at %.2f MB/s (ETA: %.1fs)",speed,eta)
g.Update()
}
if mode=="encrypt"{
//fmt.Println("'nonces' before RS: ",nonces)
fout.Seek(int64(700+len(metadata)),0)
fout.Write(rsEncode(keyHash,rs64_128,192))
fout.Write(rsEncode(khash_hash,rs32_128,160))
_mac,tmp := monocypher.Lock(nonces,nonce,key)
fmt.Println(_mac)
//tmp := cipher.Seal(nil,nonce,nonces,nil)
//fmt.Println("ENCRYPTED NONCES: ",tmp)
//_mac := tmp[len(tmp)-16:]
//tmp = tmp[:len(tmp)-16]
var chunk []byte
for i,j := range(tmp){
chunk = append(chunk,j)
if (i+1)%24==0{
fout.Write(rsEncode(chunk,rs24_128,152))
//fmt.Println(rsEncode(chunk,rs24_128,152))
chunk = nil
}
}
fout.Write(rsEncode(_mac,rs16_128,144))
}else{
//fmt.Println("crcHash: ",crcHash)
//fmt.Println("crc.Sum: ",crc.Sum(nil))
}
fin.Close()
fout.Close()
if split{
status = "Splitting file..."
stat,_ := os.Stat(outputFile)
size := stat.Size()
finished := 0
var splitted []string
fmt.Println(size)
chunkSize,_ := strconv.Atoi(splitSize)
fmt.Println(splitSelected)
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++{
fmt.Println(fmt.Sprintf("%s.%d",outputFile,i))
fout,_ := os.OpenFile(
fmt.Sprintf("%s.%d",outputFile,i),
os.O_RDWR|os.O_CREATE|os.O_TRUNC,
0755,
)
done := 0
for{
data := make([]byte,1048576)
read,err := fin.Read(data)
if err!=nil{
break
}
if !working{
fin.Close()
fout.Close()
_status = "Operation cancelled by user."
_status_color = color.RGBA{0xff,0xff,0xff,255}
for _,j := range(splitted){
os.Remove(j)
}
os.Remove(fmt.Sprintf("%s.%d",outputFile,i))
fmt.Println("outputFile",outputFile)
os.Remove(outputFile)
fast = _fast
debug.FreeOSMemory()
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)
g.Update()
}
fin.Close()
if erase{
status = "Shredding the unsplitted file..."
progressInfo = ""
shred([]string{outputFile}[:],false)
}else{
os.Remove(outputFile)
}
}
if recombine{
os.Remove(inputFile)
}
// Delete the temporary zip file
if len(allFiles)>1{
if erase{
progressInfo = ""
status = "Shredding temporary files..."
shred([]string{outputEntry}[:],false)
}else{
os.Remove(outputEntry)
}
}
fmt.Println("==============================")
if erase{
progressInfo = ""
shred(onlyFiles,false)
shred(onlyFolders,false)
}
resetUI()
if kept{
_status = "The input is corrupted and/or modified. Please be careful."
_status_color = color.RGBA{0xff,0xff,0x00,255}
}else{
_status = "Completed."
_status_color = color.RGBA{0x00,0xff,0x00,255}
}
working = false
kept = false
key = nil
status = "Ready."
debug.FreeOSMemory()
fmt.Println("Exit goroutine")
}
func shred(names []string,separate bool){
shredTotal = 0
shredDone = 0
if separate{
shredOverlay = "Shredding..."
}
for _,name := range(names){
filepath.Walk(name,func(path string,_ os.FileInfo,err error) error{
if err!=nil{
return nil
}
stat,_ := os.Stat(path)
if !stat.IsDir(){
shredTotal++
}
return nil
})
}
for _,name := range(names){
shredding = name
if runtime.GOOS=="linux"||runtime.GOOS=="darwin"{
stat,_ := os.Stat(name)
if stat.IsDir(){
var coming []string
filepath.Walk(name,func(path string,_ os.FileInfo,err error) error{
if err!=nil{
return nil
}
fmt.Println(path)
stat,_ := os.Stat(path)
if !stat.IsDir(){
if len(coming)==128{
var wg sync.WaitGroup
for i,j := range(coming){
wg.Add(1)
go func(wg *sync.WaitGroup,id int,j string){
defer wg.Done()
cmd := exec.Command("")
if runtime.GOOS=="linux"{
cmd = exec.Command("shred","-ufvz","-n","3",j)
}else{
cmd = exec.Command("rm","-rfP",j)
}
output,err := cmd.Output()
fmt.Println(err)
fmt.Println(output)
shredding = j
shredDone++
shredUpdate(separate)
g.Update()
}(&wg,i,j)
}
wg.Wait()
coming = nil
}else{
coming = append(coming,path)
}
}
return nil
})
fmt.Println(coming)
for _,i := range(coming){
go func(){
cmd := exec.Command("")
if runtime.GOOS=="linux"{
cmd = exec.Command("shred","-ufvz","-n","3",i)
}else{
cmd = exec.Command("rm","-rfP",i)
}
output,err := cmd.Output()
fmt.Println(err)
fmt.Println(output)
shredding = i
shredDone++
shredUpdate(separate)
g.Update()
}()
}
os.RemoveAll(name)
}else{
cmd := exec.Command("")
if runtime.GOOS=="linux"{
cmd = exec.Command("shred","-ufvz","-n","3",name)
}else{
cmd = exec.Command("rm","-rfP",name)
}
cmd.Run()
shredding = name+"/*"
shredDone++
shredUpdate(separate)
}
}else if runtime.GOOS=="windows"{
stat,_ := os.Stat(name)
if stat.IsDir(){
filepath.Walk(name,func(path string,_ os.FileInfo,err error) error{
if err!=nil{
return nil
}
fmt.Println(path)
stat,_ := os.Stat(path)
if stat.IsDir(){
t := 0
files,_ := ioutil.ReadDir(path)
for _,f := range(files){
if !f.IsDir(){
t++
}
}
shredDone += float32(t)
shredUpdate(separate)
tmp := filepath.Join(rootDir,"sdelete64.exe")
cmd := exec.Command(tmp,"*","-p","4")
cmd.Dir = path
cmd.Run()
shredding = strings.ReplaceAll(path,"\\","/")+"/*"
}
return nil
})
os.RemoveAll(name)
}else{
o,e := exec.Command(filepath.Join(rootDir,"sdelete64.exe"),name,"-p","4").Output()
fmt.Println(string(o),e)
shredDone++
shredUpdate(separate)
}
}
fmt.Println(name)
g.Update()
}
/*if itemSelected==1&&runtime.GOOS=="windows"{
cmd := exec.Command("cipher","/w:\""+name+"\"")
stdout,err := cmd.Output()
if err!=nil{
fmt.Println(err)
}
fmt.Println(string(stdout))
}*/
shredding = "Ready."
shredProgress = 0
shredOverlay = ""
}
func shredUpdate(separate bool){
if separate{
shredOverlay = fmt.Sprintf("%d/%d",int(shredDone),int(shredTotal))
shredProgress = shredDone/shredTotal
}else{
status = fmt.Sprintf("%d/%d",int(shredDone),int(shredTotal))
progress = shredDone/shredTotal
}
g.Update()
}
// Generate file checksums
func generateChecksums(file string){
fin,_ := os.Open(file)
cs_md5 = ""
cs_sha1 = ""
cs_sha256 = ""
cs_sha3_256 = ""
cs_blake2b = ""
cs_blake2s = ""
cs_blake3 = ""
if md5_selected{
cs_md5 = "Calculating..."
}
if sha1_selected{
cs_sha1 = "Calculating..."
}
if sha256_selected{
cs_sha256 = "Calculating..."
}
if sha3_256_selected{
cs_sha3_256 = "Calculating..."
}
if blake2b_selected{
cs_blake2b = "Calculating..."
}
if blake2s_selected{
cs_blake2s = "Calculating..."
}
if blake3_selected{
cs_blake3 = "Calculating..."
}
crc_md5 := md5.New()
crc_sha1 := sha1.New()
crc_sha256 := sha256.New()
crc_sha3_256 := sha3.New256()
crc_blake2b,_ := blake2b.New256(nil)
crc_blake2s,_ := blake2s.New256(nil)
crc_blake3 := blake3.New()
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 md5_selected{
crc_md5.Write(data)
}
if sha1_selected{
crc_sha1.Write(data)
}
if sha256_selected{
crc_sha256.Write(data)
}
if sha3_256_selected{
crc_sha3_256.Write(data)
}
if blake2b_selected{
crc_blake2b.Write(data)
}
if blake2s_selected{
crc_blake2s.Write(data)
}
if blake3_selected{
crc_blake3.Write(data)
}
done += int64(size)
cs_progress = float32(done)/float32(total)
g.Update()
}
cs_progress = 0
if md5_selected{
cs_md5 = hex.EncodeToString(crc_md5.Sum(nil))
}
if sha1_selected{
cs_sha1 = hex.EncodeToString(crc_sha1.Sum(nil))
}
if sha256_selected{
cs_sha256 = hex.EncodeToString(crc_sha256.Sum(nil))
}
if sha3_256_selected{
cs_sha3_256 = hex.EncodeToString(crc_sha3_256.Sum(nil))
}
if blake2b_selected{
cs_blake2b = hex.EncodeToString(crc_blake2b.Sum(nil))
}
if blake2s_selected{
cs_blake2s = hex.EncodeToString(crc_blake2s.Sum(nil))
}
if blake3_selected{
cs_blake3 = hex.EncodeToString(crc_blake3.Sum(nil))
}
g.Update()
}
// Reset the UI to a clean state with no nothing selected
func resetUI(){
inputLabel = "Drag and drop file(s) and folder(s) into this window."
outputEntry = ""
orLabel = "or"
outputWidth = 376
password = ""
cPassword = ""
keyfilePrompt = "Keyfile (optional):"
keyfileLabel = "Use a keyfile"
keyfile = false
metadata = ""
keep = false
erase = false
reedsolo = false
split = false
splitSize = ""
fast = false
progress = 0
progressInfo = ""
_status = ""
_status_color = color.RGBA{0xff,0xff,0xff,255}
g.Update()
}
func broken(){
working = false
_status = "The file is either corrupted or intentionally modified."
_status_color = color.RGBA{0xff,0x00,0x00,255}
os.Remove(outputFile)
debug.FreeOSMemory()
}
func rsEncode(data []byte,encoder reedsolomon.Encoder,size int) []byte{
shards,_ := encoder.Split(data)
encoder.Encode(shards)
tmp := make([]byte,size)
for i,shard := range(shards){
tmp[i] = shard[0]
}
return tmp
}
func rsDecode(data []byte,encoder reedsolomon.Encoder,size int) []byte{
res := make([][]byte,len(data))
for i,_ := range(data){
tmp := make([]byte,1)
tmp[0] = data[i]
res[i] = tmp
}
_ = encoder.Reconstruct(res)
res = res[:size]
tmp := make([]byte,size)
for i,shard := range(res){
tmp[i] = shard[0]
}
return tmp
}
// Create the master window, set callbacks, and start the UI
func main(){
di.Init()
if runtime.GOOS=="windows"{
exec.Command(filepath.Join(rootDir,"sdelete64.exe"),"/accepteula")
}
window := g.NewMasterWindow("Picocrypt",480,488,g.MasterWindowFlagsNotResizable,nil)
window.SetDropCallback(onDrop)
dpi = g.Context.GetPlatform().GetContentScale()
window.Run(startUI)
}