mirror of
https://github.com/HACKERALERT/Picocrypt.git
synced 2024-12-28 10:24:19 +00:00
Add features
This commit is contained in:
parent
71b03ef7d4
commit
71bd746f9a
1 changed files with 369 additions and 125 deletions
494
src/Picocrypt.go
494
src/Picocrypt.go
|
@ -109,6 +109,8 @@ var commentsDisabled bool
|
||||||
// Advanced options
|
// Advanced options
|
||||||
var paranoid bool
|
var paranoid bool
|
||||||
var reedsolo bool
|
var reedsolo bool
|
||||||
|
var deniability bool
|
||||||
|
var recursively bool
|
||||||
var split bool
|
var split bool
|
||||||
var splitSize string
|
var splitSize string
|
||||||
var splitUnits = []string{"KiB", "MiB", "GiB", "TiB", "Total"}
|
var splitUnits = []string{"KiB", "MiB", "GiB", "TiB", "Total"}
|
||||||
|
@ -412,7 +414,7 @@ func draw() {
|
||||||
),
|
),
|
||||||
|
|
||||||
giu.Dummy(0, 0),
|
giu.Dummy(0, 0),
|
||||||
giu.Style().SetDisabled(mode == "decrypt" && !keyfile).To(
|
giu.Style().SetDisabled(mode == "decrypt" && !keyfile && !deniability).To(
|
||||||
giu.Row(
|
giu.Row(
|
||||||
giu.Label("Keyfiles:"),
|
giu.Label("Keyfiles:"),
|
||||||
giu.Button("Edit").Size(54, 0).OnClick(func() {
|
giu.Button("Edit").Size(54, 0).OnClick(func() {
|
||||||
|
@ -484,16 +486,18 @@ func draw() {
|
||||||
giu.Checkbox("Paranoid mode", ¶noid),
|
giu.Checkbox("Paranoid mode", ¶noid),
|
||||||
giu.Tooltip("Provides the highest level of security attainable."),
|
giu.Tooltip("Provides the highest level of security attainable."),
|
||||||
giu.Dummy(-170, 0),
|
giu.Dummy(-170, 0),
|
||||||
giu.Checkbox("Compress files", &compress).OnChange(func() {
|
giu.Style().SetDisabled(recursively).To(
|
||||||
if !(len(allFiles) > 1 || len(onlyFolders) > 0) {
|
giu.Checkbox("Compress files", &compress).OnChange(func() {
|
||||||
if compress {
|
if !(len(allFiles) > 1 || len(onlyFolders) > 0) {
|
||||||
outputFile = filepath.Join(filepath.Dir(outputFile), "Encrypted") + ".zip.pcv"
|
if compress {
|
||||||
} else {
|
outputFile = filepath.Join(filepath.Dir(outputFile), "Encrypted") + ".zip.pcv"
|
||||||
outputFile = filepath.Join(filepath.Dir(outputFile), filepath.Base(inputFile)) + ".pcv"
|
} else {
|
||||||
|
outputFile = filepath.Join(filepath.Dir(outputFile), filepath.Base(inputFile)) + ".pcv"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
}),
|
giu.Tooltip("Compress files with Deflate before encrypting."),
|
||||||
giu.Tooltip("Compress files with Deflate before encrypting."),
|
),
|
||||||
).Build()
|
).Build()
|
||||||
|
|
||||||
giu.Row(
|
giu.Row(
|
||||||
|
@ -504,6 +508,18 @@ func draw() {
|
||||||
giu.Tooltip("Delete the input files after encryption."),
|
giu.Tooltip("Delete the input files after encryption."),
|
||||||
).Build()
|
).Build()
|
||||||
|
|
||||||
|
giu.Row(
|
||||||
|
giu.Checkbox("Deniability", &deniability),
|
||||||
|
giu.Tooltip("Add plausible deniability to the volume."),
|
||||||
|
giu.Dummy(-170, 0),
|
||||||
|
giu.Style().SetDisabled(!(len(allFiles) > 1 || len(onlyFolders) > 0)).To(
|
||||||
|
giu.Checkbox("Recursively", &recursively).OnChange(func() {
|
||||||
|
compress = false
|
||||||
|
}),
|
||||||
|
giu.Tooltip("Encrypt and decrypt recursive files individually."),
|
||||||
|
),
|
||||||
|
).Build()
|
||||||
|
|
||||||
giu.Row(
|
giu.Row(
|
||||||
giu.Checkbox("Split into chunks:", &split),
|
giu.Checkbox("Split into chunks:", &split),
|
||||||
giu.Tooltip("Split the output file into smaller chunks."),
|
giu.Tooltip("Split the output file into smaller chunks."),
|
||||||
|
@ -517,8 +533,10 @@ func draw() {
|
||||||
).Build()
|
).Build()
|
||||||
} else {
|
} else {
|
||||||
giu.Row(
|
giu.Row(
|
||||||
giu.Checkbox("Force decrypt", &keep),
|
giu.Style().SetDisabled(deniability).To(
|
||||||
giu.Tooltip("Override security measures when decrypting."),
|
giu.Checkbox("Force decrypt", &keep),
|
||||||
|
giu.Tooltip("Override security measures when decrypting."),
|
||||||
|
),
|
||||||
giu.Dummy(-170, 0),
|
giu.Dummy(-170, 0),
|
||||||
giu.Checkbox("Delete volume", &delete),
|
giu.Checkbox("Delete volume", &delete),
|
||||||
giu.Tooltip("Delete the volume after a successful decryption."),
|
giu.Tooltip("Delete the volume after a successful decryption."),
|
||||||
|
@ -526,77 +544,87 @@ func draw() {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
giu.Label("Save output as:"),
|
giu.Style().SetDisabled(recursively).To(
|
||||||
giu.Custom(func() {
|
giu.Label("Save output as:"),
|
||||||
w, _ := giu.GetAvailableRegion()
|
giu.Custom(func() {
|
||||||
bw, _ := giu.CalcTextSize("Change")
|
w, _ := giu.GetAvailableRegion()
|
||||||
p, _ := giu.GetWindowPadding()
|
bw, _ := giu.CalcTextSize("Change")
|
||||||
bw += p * 2
|
p, _ := giu.GetWindowPadding()
|
||||||
dw := w - bw - p
|
bw += p * 2
|
||||||
giu.Style().SetDisabled(true).To(
|
dw := w - bw - p
|
||||||
giu.InputText(func() *string {
|
giu.Style().SetDisabled(true).To(
|
||||||
tmp := ""
|
giu.InputText(func() *string {
|
||||||
if outputFile == "" {
|
tmp := ""
|
||||||
|
if outputFile == "" {
|
||||||
|
return &tmp
|
||||||
|
}
|
||||||
|
tmp = filepath.Base(outputFile)
|
||||||
|
if split {
|
||||||
|
tmp += ".*"
|
||||||
|
}
|
||||||
|
if recursively {
|
||||||
|
tmp = "(multiple values)"
|
||||||
|
}
|
||||||
return &tmp
|
return &tmp
|
||||||
|
}()).Size(dw / dpi / dpi).Flags(16384),
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
giu.SameLine()
|
||||||
|
giu.Button("Change").Size(bw/dpi, 0).OnClick(func() {
|
||||||
|
f := dialog.File().Title("Choose where to save the output. Don't include extensions.")
|
||||||
|
f.SetStartDir(func() string {
|
||||||
|
if len(onlyFiles) > 0 {
|
||||||
|
return filepath.Dir(onlyFiles[0])
|
||||||
|
}
|
||||||
|
return filepath.Dir(onlyFolders[0])
|
||||||
|
}())
|
||||||
|
|
||||||
|
// Prefill the filename
|
||||||
|
tmp := strings.TrimSuffix(filepath.Base(outputFile), ".pcv")
|
||||||
|
f.SetInitFilename(strings.TrimSuffix(tmp, filepath.Ext(tmp)))
|
||||||
|
if mode == "encrypt" && (len(allFiles) > 1 || len(onlyFolders) > 0 || compress) {
|
||||||
|
f.SetInitFilename("Encrypted")
|
||||||
}
|
}
|
||||||
tmp = filepath.Base(outputFile)
|
|
||||||
if split {
|
// Get the chosen file path
|
||||||
tmp += ".*"
|
file, err := f.Save()
|
||||||
|
if file == "" || err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return &tmp
|
file = filepath.Join(filepath.Dir(file), strings.Split(filepath.Base(file), ".")[0])
|
||||||
}()).Size(dw / dpi / dpi).Flags(16384),
|
|
||||||
).Build()
|
|
||||||
|
|
||||||
giu.SameLine()
|
// Add the correct extensions
|
||||||
giu.Button("Change").Size(bw/dpi, 0).OnClick(func() {
|
if mode == "encrypt" {
|
||||||
f := dialog.File().Title("Choose where to save the output. Don't include extensions.")
|
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||||
f.SetStartDir(func() string {
|
file += ".zip.pcv"
|
||||||
if len(onlyFiles) > 0 {
|
} else {
|
||||||
return filepath.Dir(onlyFiles[0])
|
file += filepath.Ext(inputFile) + ".pcv"
|
||||||
}
|
}
|
||||||
return filepath.Dir(onlyFolders[0])
|
|
||||||
}())
|
|
||||||
|
|
||||||
// Prefill the filename
|
|
||||||
tmp := strings.TrimSuffix(filepath.Base(outputFile), ".pcv")
|
|
||||||
f.SetInitFilename(strings.TrimSuffix(tmp, filepath.Ext(tmp)))
|
|
||||||
if mode == "encrypt" && (len(allFiles) > 1 || len(onlyFolders) > 0 || compress) {
|
|
||||||
f.SetInitFilename("Encrypted")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the chosen file path
|
|
||||||
file, err := f.Save()
|
|
||||||
if file == "" || err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
file = filepath.Join(filepath.Dir(file), strings.Split(filepath.Base(file), ".")[0])
|
|
||||||
|
|
||||||
// Add the correct extensions
|
|
||||||
if mode == "encrypt" {
|
|
||||||
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
|
||||||
file += ".zip.pcv"
|
|
||||||
} else {
|
} else {
|
||||||
file += filepath.Ext(inputFile) + ".pcv"
|
if strings.HasSuffix(inputFile, ".zip.pcv") {
|
||||||
|
file += ".zip"
|
||||||
|
} else {
|
||||||
|
tmp := strings.TrimSuffix(filepath.Base(inputFile), ".pcv")
|
||||||
|
file += filepath.Ext(tmp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
outputFile = file
|
||||||
if strings.HasSuffix(inputFile, ".zip.pcv") {
|
mainStatus = "Ready."
|
||||||
file += ".zip"
|
mainStatusColor = WHITE
|
||||||
} else {
|
}).Build()
|
||||||
tmp := strings.TrimSuffix(filepath.Base(inputFile), ".pcv")
|
giu.Tooltip("Save the output with a custom name and path.").Build()
|
||||||
file += filepath.Ext(tmp)
|
}),
|
||||||
}
|
),
|
||||||
}
|
|
||||||
outputFile = file
|
|
||||||
mainStatus = "Ready."
|
|
||||||
mainStatusColor = WHITE
|
|
||||||
}).Build()
|
|
||||||
giu.Tooltip("Save the output with a custom name and path.").Build()
|
|
||||||
}),
|
|
||||||
|
|
||||||
giu.Dummy(0, 0),
|
giu.Dummy(0, 0),
|
||||||
giu.Separator(),
|
giu.Separator(),
|
||||||
giu.Dummy(0, 0),
|
giu.Dummy(0, 0),
|
||||||
giu.Button(startLabel).Size(giu.Auto, 34).OnClick(func() {
|
giu.Button(func() string {
|
||||||
|
if !recursively {
|
||||||
|
return startLabel
|
||||||
|
}
|
||||||
|
return "Work"
|
||||||
|
}()).Size(giu.Auto, 34).OnClick(func() {
|
||||||
if keyfile && keyfiles == nil {
|
if keyfile && keyfiles == nil {
|
||||||
mainStatus = "Please select your keyfiles."
|
mainStatus = "Please select your keyfiles."
|
||||||
mainStatusColor = RED
|
mainStatusColor = RED
|
||||||
|
@ -623,7 +651,7 @@ func draw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If files already exist, show the overwrite modal
|
// If files already exist, show the overwrite modal
|
||||||
if err == nil {
|
if err == nil && !recursively {
|
||||||
showOverwrite = true
|
showOverwrite = true
|
||||||
modalId++
|
modalId++
|
||||||
giu.Update()
|
giu.Update()
|
||||||
|
@ -633,12 +661,64 @@ func draw() {
|
||||||
canCancel = true
|
canCancel = true
|
||||||
modalId++
|
modalId++
|
||||||
giu.Update()
|
giu.Update()
|
||||||
go func() {
|
if !recursively {
|
||||||
work()
|
go func() {
|
||||||
working = false
|
work()
|
||||||
showProgress = false
|
working = false
|
||||||
giu.Update()
|
showProgress = false
|
||||||
}()
|
giu.Update()
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
// Store variables as they will be cleared
|
||||||
|
oldPassword := password
|
||||||
|
oldKeyfile := keyfile
|
||||||
|
oldKeyfiles := keyfiles
|
||||||
|
oldKeyfileOrdered := keyfileOrdered
|
||||||
|
oldKeyfileLabel := keyfileLabel
|
||||||
|
oldComments := comments
|
||||||
|
oldParanoid := paranoid
|
||||||
|
oldReedsolo := reedsolo
|
||||||
|
oldDeniability := deniability
|
||||||
|
oldSplit := split
|
||||||
|
oldSplitSize := splitSize
|
||||||
|
oldSplitSelected := splitSelected
|
||||||
|
oldDelete := delete
|
||||||
|
files := allFiles
|
||||||
|
go func() {
|
||||||
|
for _, file := range files {
|
||||||
|
// Simulate dropping the file
|
||||||
|
onDrop([]string{file})
|
||||||
|
|
||||||
|
// Restore variables and options
|
||||||
|
password = oldPassword
|
||||||
|
cpassword = oldPassword
|
||||||
|
keyfile = oldKeyfile
|
||||||
|
keyfiles = oldKeyfiles
|
||||||
|
keyfileOrdered = oldKeyfileOrdered
|
||||||
|
keyfileLabel = oldKeyfileLabel
|
||||||
|
comments = oldComments
|
||||||
|
paranoid = oldParanoid
|
||||||
|
reedsolo = oldReedsolo
|
||||||
|
deniability = oldDeniability
|
||||||
|
split = oldSplit
|
||||||
|
splitSize = oldSplitSize
|
||||||
|
splitSelected = oldSplitSelected
|
||||||
|
delete = oldDelete
|
||||||
|
|
||||||
|
work()
|
||||||
|
if !working {
|
||||||
|
resetUI()
|
||||||
|
cancel(nil, nil)
|
||||||
|
showProgress = false
|
||||||
|
giu.Update()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
working = false
|
||||||
|
showProgress = false
|
||||||
|
giu.Update()
|
||||||
|
}()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
giu.Style().SetColor(giu.StyleColorText, mainStatusColor).To(
|
giu.Style().SetColor(giu.StyleColorText, mainStatusColor).To(
|
||||||
|
@ -771,59 +851,58 @@ func onDrop(names []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use regex to test if the input is a valid Picocrypt volume
|
// Check if version can be read from header
|
||||||
tmp := make([]byte, 15)
|
tmp := make([]byte, 15)
|
||||||
fin.Read(tmp)
|
fin.Read(tmp)
|
||||||
tmp, err = rsDecode(rs5, tmp)
|
tmp, err = rsDecode(rs5, tmp)
|
||||||
if valid, _ := regexp.Match(`^v1\.\d{2}`, tmp); !valid || err != nil {
|
if valid, _ := regexp.Match(`^v1\.\d{2}`, tmp); !valid || err != nil {
|
||||||
resetUI()
|
// Volume has plausible deniability
|
||||||
mainStatus = "This doesn't seem like a Picocrypt volume."
|
deniability = true
|
||||||
mainStatusColor = RED
|
mainStatus = "Can't read header, assuming volume is deniable."
|
||||||
fin.Close()
|
fin.Close()
|
||||||
return
|
} else {
|
||||||
}
|
// Read comments from file and check for corruption
|
||||||
|
tmp = make([]byte, 15)
|
||||||
// Read comments from file and check for corruption
|
|
||||||
tmp = make([]byte, 15)
|
|
||||||
fin.Read(tmp)
|
|
||||||
tmp, err = rsDecode(rs5, tmp)
|
|
||||||
if err == nil {
|
|
||||||
commentsLength, _ := strconv.Atoi(string(tmp))
|
|
||||||
tmp = make([]byte, commentsLength*3)
|
|
||||||
fin.Read(tmp)
|
fin.Read(tmp)
|
||||||
comments = ""
|
tmp, err = rsDecode(rs5, tmp)
|
||||||
for i := 0; i < commentsLength*3; i += 3 {
|
if err == nil {
|
||||||
t, err := rsDecode(rs1, tmp[i:i+3])
|
commentsLength, _ := strconv.Atoi(string(tmp))
|
||||||
if err != nil {
|
tmp = make([]byte, commentsLength*3)
|
||||||
comments = "Comments are corrupted."
|
fin.Read(tmp)
|
||||||
break
|
comments = ""
|
||||||
|
for i := 0; i < commentsLength*3; i += 3 {
|
||||||
|
t, err := rsDecode(rs1, tmp[i:i+3])
|
||||||
|
if err != nil {
|
||||||
|
comments = "Comments are corrupted."
|
||||||
|
break
|
||||||
|
}
|
||||||
|
comments += string(t)
|
||||||
}
|
}
|
||||||
comments += string(t)
|
} else {
|
||||||
|
comments = "Comments are corrupted."
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
comments = "Comments are corrupted."
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read flags from file and check for corruption
|
// Read flags from file and check for corruption
|
||||||
flags := make([]byte, 15)
|
flags := make([]byte, 15)
|
||||||
fin.Read(flags)
|
fin.Read(flags)
|
||||||
fin.Close()
|
fin.Close()
|
||||||
flags, err = rsDecode(rs5, flags)
|
flags, err = rsDecode(rs5, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mainStatus = "The volume header is damaged."
|
mainStatus = "The volume header is damaged."
|
||||||
mainStatusColor = RED
|
mainStatusColor = RED
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update UI and variables according to flags
|
// Update UI and variables according to flags
|
||||||
if flags[1] == 1 {
|
if flags[1] == 1 {
|
||||||
keyfile = true
|
keyfile = true
|
||||||
keyfileLabel = "Keyfiles required."
|
keyfileLabel = "Keyfiles required."
|
||||||
} else {
|
} else {
|
||||||
keyfileLabel = "Not applicable."
|
keyfileLabel = "Not applicable."
|
||||||
}
|
}
|
||||||
if flags[2] == 1 {
|
if flags[2] == 1 {
|
||||||
keyfileOrdered = true
|
keyfileOrdered = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else { // One file was dropped for encryption
|
} else { // One file was dropped for encryption
|
||||||
mode = "encrypt"
|
mode = "encrypt"
|
||||||
|
@ -1087,6 +1166,85 @@ func work() {
|
||||||
inputFile = outputFile + ".pcv"
|
inputFile = outputFile + ".pcv"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Input volume has plausible deniability
|
||||||
|
if mode == "decrypt" && deniability {
|
||||||
|
popupStatus = "Removing deniability protection..."
|
||||||
|
progressInfo = ""
|
||||||
|
progress = 0
|
||||||
|
canCancel = false
|
||||||
|
giu.Update()
|
||||||
|
|
||||||
|
// Get size of volume for showing progress
|
||||||
|
stat, _ := os.Stat(inputFile)
|
||||||
|
total := stat.Size()
|
||||||
|
|
||||||
|
// Rename input volume to free up the filename
|
||||||
|
fin, _ := os.Open(inputFile)
|
||||||
|
for strings.HasSuffix(inputFile, ".tmp") {
|
||||||
|
inputFile = strings.TrimSuffix(inputFile, ".tmp")
|
||||||
|
}
|
||||||
|
inputFile += ".tmp"
|
||||||
|
fout, _ := os.Create(inputFile)
|
||||||
|
|
||||||
|
// Get the Argon2 salt and XChaCha20 nonce from input volume
|
||||||
|
salt := make([]byte, 16)
|
||||||
|
nonce := make([]byte, 24)
|
||||||
|
fin.Read(salt)
|
||||||
|
fin.Read(nonce)
|
||||||
|
|
||||||
|
// Generate key and XChaCha20
|
||||||
|
key := argon2.IDKey([]byte(password), salt, 4, 1<<20, 4, 32)
|
||||||
|
chacha, _ := chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||||
|
|
||||||
|
// Decrypt the entire volume
|
||||||
|
done, counter := 0, 0
|
||||||
|
for {
|
||||||
|
src := make([]byte, MiB)
|
||||||
|
size, err := fin.Read(src)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
src = src[:size]
|
||||||
|
dst := make([]byte, len(src))
|
||||||
|
chacha.XORKeyStream(dst, src)
|
||||||
|
fout.Write(dst)
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
done += size
|
||||||
|
counter += MiB
|
||||||
|
progress = float32(float64(done) / float64(total))
|
||||||
|
giu.Update()
|
||||||
|
|
||||||
|
// Change nonce after 60 GiB to prevent overflow
|
||||||
|
if counter >= 60*GiB {
|
||||||
|
tmp := sha3.New256()
|
||||||
|
tmp.Write(nonce)
|
||||||
|
nonce = tmp.Sum(nil)[:24]
|
||||||
|
chacha, _ = chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||||
|
counter = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fin.Close()
|
||||||
|
fout.Close()
|
||||||
|
|
||||||
|
// Check if the version can be read from the volume
|
||||||
|
fin, _ = os.Open(inputFile)
|
||||||
|
tmp := make([]byte, 15)
|
||||||
|
fin.Read(tmp)
|
||||||
|
fin.Close()
|
||||||
|
tmp, err := rsDecode(rs5, tmp)
|
||||||
|
if valid, _ := regexp.Match(`^v1\.\d{2}`, tmp); !valid || err != nil {
|
||||||
|
os.Remove(inputFile)
|
||||||
|
inputFile = strings.TrimSuffix(inputFile, ".tmp")
|
||||||
|
broken(nil, nil, "Password is incorrect or the file is not a volume.", true)
|
||||||
|
if recombine {
|
||||||
|
inputFile = inputFileOld
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
canCancel = false
|
canCancel = false
|
||||||
progress = 0
|
progress = 0
|
||||||
progressInfo = ""
|
progressInfo = ""
|
||||||
|
@ -1232,6 +1390,10 @@ func work() {
|
||||||
paranoid = flags[0] == 1
|
paranoid = flags[0] == 1
|
||||||
reedsolo = flags[3] == 1
|
reedsolo = flags[3] == 1
|
||||||
padded = flags[4] == 1
|
padded = flags[4] == 1
|
||||||
|
if deniability {
|
||||||
|
keyfile = flags[1] == 1
|
||||||
|
keyfileOrdered = flags[2] == 1
|
||||||
|
}
|
||||||
|
|
||||||
salt = make([]byte, 48)
|
salt = make([]byte, 48)
|
||||||
fin.Read(salt)
|
fin.Read(salt)
|
||||||
|
@ -1365,7 +1527,7 @@ func work() {
|
||||||
keyCorrect := subtle.ConstantTimeCompare(keyHash, keyHashRef) == 1
|
keyCorrect := subtle.ConstantTimeCompare(keyHash, keyHashRef) == 1
|
||||||
keyfileCorrect := subtle.ConstantTimeCompare(keyfileHash, keyfileHashRef) == 1
|
keyfileCorrect := subtle.ConstantTimeCompare(keyfileHash, keyfileHashRef) == 1
|
||||||
incorrect := !keyCorrect
|
incorrect := !keyCorrect
|
||||||
if keyfile {
|
if keyfile || len(keyfiles) > 0 {
|
||||||
incorrect = !keyCorrect || !keyfileCorrect
|
incorrect = !keyCorrect || !keyfileCorrect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1382,8 +1544,16 @@ func work() {
|
||||||
} else {
|
} else {
|
||||||
mainStatus = "Incorrect keyfiles."
|
mainStatus = "Incorrect keyfiles."
|
||||||
}
|
}
|
||||||
|
if deniability {
|
||||||
|
fin.Close()
|
||||||
|
os.Remove(inputFile)
|
||||||
|
inputFile = strings.TrimSuffix(inputFile, ".tmp")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
broken(fin, nil, mainStatus, true)
|
broken(fin, nil, mainStatus, true)
|
||||||
|
if recombine {
|
||||||
|
inputFile = inputFileOld
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1648,6 +1818,69 @@ func work() {
|
||||||
fin.Close()
|
fin.Close()
|
||||||
fout.Close()
|
fout.Close()
|
||||||
|
|
||||||
|
// Add plausible deniability
|
||||||
|
if mode == "encrypt" && deniability {
|
||||||
|
popupStatus = "Adding plausible deniability..."
|
||||||
|
canCancel = false
|
||||||
|
giu.Update()
|
||||||
|
|
||||||
|
// Get size of volume for showing progress
|
||||||
|
stat, _ := os.Stat(fout.Name())
|
||||||
|
total := stat.Size()
|
||||||
|
|
||||||
|
// Rename the output volume to free up the filename
|
||||||
|
os.Rename(fout.Name(), fout.Name()+".tmp")
|
||||||
|
fin, _ := os.Open(fout.Name() + ".tmp")
|
||||||
|
fout, _ := os.Create(fout.Name())
|
||||||
|
|
||||||
|
// Use a random Argon2 salt and XChaCha20 nonce
|
||||||
|
salt := make([]byte, 16)
|
||||||
|
nonce := make([]byte, 24)
|
||||||
|
rand.Read(salt)
|
||||||
|
rand.Read(nonce)
|
||||||
|
fout.Write(salt)
|
||||||
|
fout.Write(nonce)
|
||||||
|
|
||||||
|
// Generate key and XChaCha20
|
||||||
|
key := argon2.IDKey([]byte(password), salt, 4, 1<<20, 4, 32)
|
||||||
|
chacha, _ := chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||||
|
|
||||||
|
// Encrypt the entire volume
|
||||||
|
done, counter := 0, 0
|
||||||
|
for {
|
||||||
|
src := make([]byte, MiB)
|
||||||
|
size, err := fin.Read(src)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
src = src[:size]
|
||||||
|
dst := make([]byte, len(src))
|
||||||
|
chacha.XORKeyStream(dst, src)
|
||||||
|
fout.Write(dst)
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
done += size
|
||||||
|
counter += MiB
|
||||||
|
progress = float32(float64(done) / float64(total))
|
||||||
|
giu.Update()
|
||||||
|
|
||||||
|
// Change nonce after 60 GiB to prevent overflow
|
||||||
|
if counter >= 60*GiB {
|
||||||
|
tmp := sha3.New256()
|
||||||
|
tmp.Write(nonce)
|
||||||
|
nonce = tmp.Sum(nil)[:24]
|
||||||
|
chacha, _ = chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||||
|
counter = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fin.Close()
|
||||||
|
fout.Close()
|
||||||
|
os.Remove(fin.Name())
|
||||||
|
canCancel = true
|
||||||
|
giu.Update()
|
||||||
|
}
|
||||||
|
|
||||||
// Split the file into chunks
|
// Split the file into chunks
|
||||||
if split {
|
if split {
|
||||||
var splitted []string
|
var splitted []string
|
||||||
|
@ -1764,6 +1997,9 @@ func work() {
|
||||||
// Delete temporary files used during encryption and decryption
|
// Delete temporary files used during encryption and decryption
|
||||||
if recombine || len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
if recombine || len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||||
os.Remove(inputFile)
|
os.Remove(inputFile)
|
||||||
|
if deniability {
|
||||||
|
os.Remove(strings.TrimSuffix(inputFile, ".tmp"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the input files if the user chooses
|
// Delete the input files if the user chooses
|
||||||
|
@ -1784,6 +2020,9 @@ func work() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
os.Remove(inputFile)
|
os.Remove(inputFile)
|
||||||
|
if deniability {
|
||||||
|
os.Remove(strings.TrimSuffix(inputFile, ".tmp"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, i := range onlyFiles {
|
for _, i := range onlyFiles {
|
||||||
|
@ -1794,6 +2033,9 @@ func work() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if mode == "decrypt" && deniability {
|
||||||
|
os.Remove(inputFile)
|
||||||
|
}
|
||||||
|
|
||||||
// All done, reset the UI
|
// All done, reset the UI
|
||||||
oldKept := kept
|
oldKept := kept
|
||||||
|
@ -1884,6 +2126,8 @@ func resetUI() {
|
||||||
|
|
||||||
paranoid = false
|
paranoid = false
|
||||||
reedsolo = false
|
reedsolo = false
|
||||||
|
deniability = false
|
||||||
|
recursively = false
|
||||||
split = false
|
split = false
|
||||||
splitSize = ""
|
splitSize = ""
|
||||||
splitSelected = 1
|
splitSelected = 1
|
||||||
|
@ -2013,7 +2257,7 @@ func sizeify(size int64) string {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create the main window
|
// Create the main window
|
||||||
window = giu.NewMasterWindow("Picocrypt", 318, 479, giu.MasterWindowFlagsNotResizable)
|
window = giu.NewMasterWindow("Picocrypt", 318, 507, giu.MasterWindowFlagsNotResizable)
|
||||||
|
|
||||||
// Start the dialog module
|
// Start the dialog module
|
||||||
dialog.Init()
|
dialog.Init()
|
||||||
|
|
Loading…
Reference in a new issue