mirror of
https://github.com/Picocrypt/Picocrypt.git
synced 2025-05-13 06:08:30 +02:00
Compare commits
13 commits
b2daa11a1c
...
504e908195
Author | SHA1 | Date | |
---|---|---|---|
|
504e908195 | ||
|
9b2b69e442 | ||
|
f58f5ce249 | ||
|
b26137d959 | ||
|
065a50d90e | ||
|
c63cf92672 | ||
|
6a8fdeaa53 | ||
|
bad71f95ce | ||
|
d7a0ee126b | ||
|
16bb70dc97 | ||
|
9e7e2e9c44 | ||
|
757c9c23e4 | ||
|
159944a619 |
6 changed files with 163 additions and 84 deletions
24
.github/ISSUE_TEMPLATE/default.yml
vendored
24
.github/ISSUE_TEMPLATE/default.yml
vendored
|
@ -12,11 +12,11 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Picocrypt is a "finished" piece of software and is in a maintenance-only stage. This does not mean the software is old, outdated, or abandonware, but that the focus is on fixing bugs and ensuring the software continues to work smoothly as opposed to actively developing new features. As well, my time as the developer is limited considering that Picocrypt brings me no monetary benefit and is entirely a gift of my time and skill to the community.
|
||||
Picocrypt is a "finished" piece of software and is in a maintenance-only stage. This does not mean the software is old, outdated, or abandonware, but that the sole focus is on fixing bugs and ensuring the software continues to work smoothly as opposed to actively developing new features. As well, my time as the developer is very limited considering that Picocrypt brings me no monetary benefit and is entirely a gift of my time and skill to the community.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
*To ensure that issues remain relevant and as time-efficient as possible for me, please follow the guidelines below depending on the type/topic of your issue.*
|
||||
*Therefore, to save me time so that I can focus on the important things, please follow the guidelines below depending on your topic.*
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
@ -24,7 +24,7 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
These are high-priority issues and the main purpose of this repository's issue tracker. Make the issue with a short description, and then once the issue is created, add a comment with as many details as possible. Ping me (@HACKERALERT) in the comment so that I can get to it as soon as possible. Keep in mind that I define "bug" as something wrong with Picocrypt's code itself. If it's not Picocrypt's fault, it's not a bug.
|
||||
These are important; make the issue with a short description, and then once the issue is created, add a comment with as many details as possible. Ping me (@HACKERALERT) in the comment so that I can get to it as soon as possible. Keep in mind that I define "bug" as something wrong with Picocrypt's code itself. If it's not Picocrypt's fault, it's not a bug.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
@ -32,7 +32,7 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Usually these issues are not directly caused by Picocrypt's code. Create the issue and in a separate comment, provide details about the environment you're running in (like OS, DE, etc.). **Do not ping me initially.** Let the issue sit for at least *3 days* to allow other users to potentially help you resolve the issue. If after 3 days, you haven't figured things out, then you may ping me (@HACKERALERT).
|
||||
Usually these issues are not directly caused by Picocrypt's code. If you're on Windows, see [here](https://github.com/Picocrypt/Picocrypt/issues/91). If you're on Linux, install some packages and try again (see [here](https://github.com/Picocrypt/Picocrypt/tree/main/src#1-prerequisites)). Picocrypt only targets Windows 11, Ubuntu 24/Debian 12, and macOS 15 or later, so *do not create an issue if your OS is older than those; that is your problem, not mine*. If none of the points above help, create the issue and in a separate comment, provide details about the environment you're running in (like OS, DE, etc.). **Do not ping me initially.** Let the issue sit for at least *5 days* to allow other users to potentially help you resolve the issue. If after 5 days, you haven't figured things out, then you may ping me (@HACKERALERT).
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
@ -40,7 +40,7 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
These are unpreventable; Picocrypt is cryptography, file deletion, and passwords bundled into an executable... which looks similar to ransomware, unfortunately. Please report these false positives to your antivirus software provider and do not create an issue about it.
|
||||
These are unpreventable; report them as false positives to your antivirus software provider and **do not create an issue about it**.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
@ -48,7 +48,7 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Create the issue and ask your question or support request in a separate comment. **Do not ping me initially.** Let the issue sit for at least *5 days* to give other users a chance to help you first. If after 5 days, you have not received any assistance, then you may ping me (@HACKERALERT).
|
||||
Create the issue and ask your question or support request in a separate comment. **Do not ping me initially.** Let the issue sit for at least *10 days* to give other users a chance to help you first. If after 10 days, you have not received any assistance, then you may ping me (@HACKERALERT).
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
@ -56,7 +56,7 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Picocrypt is mature software; I do not intend to add any new features. Generally, do not create any feature requests unless it's very minor and can be implemented with low effort and minimal impact on reliability and security. What is considered "minor" is subjective, but here is an example: "the ability to decrypt a volume entirely in-memory" is pretty significant, while "auto start encryption on pressing the Enter key" is relatively minor. A proof-of-concept link to code or a fork would be appreciated.
|
||||
Picocrypt is mature software; I do not intend to add any new features. **Do not create these types of issues.**
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
@ -64,7 +64,7 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Picocrypt prioritizes correctness and reliability over performance, so many parts of the code are written sequentially and don't use concurrency. This is intentional and need not be pointed out. Unless performance is absolutely atrocious to the point where it is indicative of a potential bug, do not make issues about performance.
|
||||
Picocrypt prioritizes correctness and reliability over performance, so many parts of the code are written sequentially and don't use concurrency. This is intentional and need not be pointed out. Unless performance is absolutely atrocious to the point where it is indicative of a potential bug, **do not make issues about performance**.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
@ -72,8 +72,9 @@ body:
|
|||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
You will have to use your best judgement here. Read the sections above to get an idea of what I expect to see and do what you think is best. Ideally, ping me only if sufficient time has passed for other users to assist/answer you, or it is best addressed by me directly.
|
||||
You will have to use your best judgement here. Read the sections above to get an idea of what I expect to see and do what you think is best. Ideally, ping me only if sufficient time has passed for other users to assist/answer you, or it is best addressed by me directly. You must first look through existing issues or do a web search (AI can help!) before creating the issue. While I am allowing these generic issues to be made, if they become a hassle, I reserve the right to disallow them in the future.
|
||||
- type: checkboxes
|
||||
id: confirmation
|
||||
attributes:
|
||||
label: "Please confirm:"
|
||||
options:
|
||||
|
@ -83,7 +84,12 @@ body:
|
|||
required: true
|
||||
- label: "I acknowledge my issue may be ignored or closed without explanation"
|
||||
required: true
|
||||
- label: "I have looked through previous issues and related info already"
|
||||
required: true
|
||||
- label: "I will remember to close my issue when it is resolved"
|
||||
required: true
|
||||
- type: input
|
||||
id: summary
|
||||
attributes:
|
||||
label: "Describe the issue briefly in a few sentences:"
|
||||
description: "You can add more details in a separate comment after creating the issue."
|
||||
|
|
21
.github/workflows/close-issues.yml
vendored
Normal file
21
.github/workflows/close-issues.yml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
name: Close inactive issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-issue-stale: 30
|
||||
days-before-issue-close: 14
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
14
Changelog.md
14
Changelog.md
|
@ -1,7 +1,17 @@
|
|||
# v1.48 (Released 04/11/2025)
|
||||
# Future
|
||||
<ul>
|
||||
<li>Figure out how to remove use of temporary files completely</li>
|
||||
</ul>
|
||||
|
||||
# v1.48 (Released 04/15/2025)
|
||||
<ul>
|
||||
<li>✓ Allow pressing 'Enter' key to press Start/Process button</li>
|
||||
<li>✓ Warn user when encrypting multiple files to an external drive</li>
|
||||
<li>✓ Update "Encrypt" button to "Zip and Encrypt" if multiple files</li>
|
||||
<li>✓ Give user estimated required free disk space in status label</li>
|
||||
<li>✓ Encrypt previously unencrypted temporary zip files</li>
|
||||
<li>✓ Add `.incomplete` to filenames while work is in progress</li>
|
||||
<li>✓ Use `encrypted-*.zip.pcv` output name instead of `Encrypted.zip.pcv`</li>
|
||||
<li>✓ Use 0700 permissions when auto unzipping and creating folders</li>
|
||||
</ul>
|
||||
|
||||
# v1.47 (Released 02/19/2025)
|
||||
|
|
|
@ -90,14 +90,6 @@ While being simple, Picocrypt also strives to be powerful in the hands of knowle
|
|||
<li><strong>Recursively</strong>: If you want to encrypt and/or decrypt a large set of files individually, this option will tell Picocrypt to go through every recursive file that you drop in and encrypt/decrypt it separately. This is useful, for example, if you are encrypting thousands of large documents and want to be able to decrypt any one of them in particular without having to download and decrypt the entire set of documents. Keep in mind that this is a very complex feature that should only be used if you know what you are doing.</li>
|
||||
</ul>
|
||||
|
||||
# Caveats
|
||||
When encrypting multiple files, Picocrypt will automatically zip them into one file before encrypting it. However, this requires a two-step process that creates an unencrypted temporary `.zip.tmp` file in the same destination folder. This has two implications:
|
||||
<ol>
|
||||
<li>There must be at least double the available free space on the target drive as the combined total size of input files</li>
|
||||
<li>The target drive must be safe to save confidential data; if not, the unencrypted temporary file may be recoverable even after deletion</li>
|
||||
</ol>
|
||||
To mitigate these caveats, Picocrypt will show info and warning labels accordingly. However, it is best to prevent these issues altogether <strong>by always encrypting and decrypting on your main host drive</strong> and then copying encrypted volumes to and from external sources, <strong>or zipping up input files beforehand and encrypting that single file</strong> which doesn't have these caveats.
|
||||
|
||||
# Security
|
||||
For more information on how Picocrypt handles cryptography, see <a href="Internals.md">Internals</a> for the technical details. If you're worried about the safety of me or this project, let me assure you that this repository won't be hijacked or backdoored. I have 2FA (TOTP) enabled on all accounts with a tie to Picocrypt (GitHub, etc.), in addition to full-disk encryption on all of my portable devices. For further hardening, Picocrypt uses my isolated forks of dependencies and I fetch upstream only when I have taken a look at the changes and believe that there aren't any security issues. This means that if a dependency gets hacked or deleted by the author, Picocrypt will be using my fork of it and remain completely unaffected. I've also meticulously gone through every single setting in the Picocrypt organization and repos, locking down access behind multiple layers of security such as read-only base-level member permissions, required PRs and mandatory approvals (which no one can do but me), mandatory CODEOWNERS approvals, and I'm the only member of the Picocrypt organization and repos (except for PicoGo). You can feel confident about using Picocrypt as long as you understand:
|
||||
|
||||
|
|
6
dist/windows/versioninfo.rc
vendored
6
dist/windows/versioninfo.rc
vendored
|
@ -1,6 +1,6 @@
|
|||
1 VERSIONINFO
|
||||
FILEVERSION 1,47,0,0
|
||||
PRODUCTVERSION 1,47,0,0
|
||||
FILEVERSION 1,48,0,0
|
||||
PRODUCTVERSION 1,48,0,0
|
||||
FILEOS 0x40004
|
||||
FILETYPE 0x1
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ BLOCK "StringFileInfo"
|
|||
{
|
||||
BLOCK "040904B0"
|
||||
{
|
||||
VALUE "FileVersion", "1.47"
|
||||
VALUE "FileVersion", "1.48"
|
||||
VALUE "LegalCopyright", "\xA9 Evan Su & contributors, GPLv3"
|
||||
VALUE "ProductName", "Picocrypt"
|
||||
}
|
||||
|
|
174
src/Picocrypt.go
174
src/Picocrypt.go
|
@ -30,7 +30,6 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -131,9 +130,8 @@ var startLabel = "Start"
|
|||
var mainStatus = "Ready"
|
||||
var mainStatusColor = WHITE
|
||||
var popupStatus string
|
||||
|
||||
var temporaryZip bool
|
||||
var externalDst bool
|
||||
var usingTempZip bool
|
||||
var requiredFreeSpace int64
|
||||
|
||||
// Progress variables
|
||||
var progress float32
|
||||
|
@ -177,6 +175,33 @@ func (p *compressorProgress) Read(data []byte) (int, error) {
|
|||
return read, err
|
||||
}
|
||||
|
||||
type encryptedZipWriter struct {
|
||||
_w io.Writer
|
||||
_cipher *chacha20.Cipher
|
||||
}
|
||||
|
||||
func (ezw *encryptedZipWriter) Write(data []byte) (n int, err error) {
|
||||
dst := make([]byte, len(data))
|
||||
ezw._cipher.XORKeyStream(dst, data)
|
||||
return ezw._w.Write(dst)
|
||||
}
|
||||
|
||||
type encryptedZipReader struct {
|
||||
_r io.Reader
|
||||
_cipher *chacha20.Cipher
|
||||
}
|
||||
|
||||
func (ezr *encryptedZipReader) Read(data []byte) (n int, err error) {
|
||||
src := make([]byte, len(data))
|
||||
n, err = ezr._r.Read(src)
|
||||
if err == nil && n > 0 {
|
||||
dst := make([]byte, n)
|
||||
ezr._cipher.XORKeyStream(dst, src[:n])
|
||||
copy(data, dst)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
var onClickStartButton = func() {
|
||||
// Start button should be disabled if these conditions are true; don't do anything if so
|
||||
if (len(keyfiles) == 0 && password == "") || (mode == "encrypt" && password != cpassword) {
|
||||
|
@ -611,15 +636,7 @@ func draw() {
|
|||
giu.Tooltip("Provides the highest level of security attainable"),
|
||||
giu.Dummy(-170, 0),
|
||||
giu.Style().SetDisabled(recursively || !(len(allFiles) > 1 || len(onlyFolders) > 0)).To(
|
||||
giu.Checkbox("Compress files", &compress).OnChange(func() {
|
||||
if !(len(allFiles) > 1 || len(onlyFolders) > 0) {
|
||||
if compress {
|
||||
outputFile = filepath.Join(filepath.Dir(outputFile), "Encrypted") + ".zip.pcv"
|
||||
} else {
|
||||
outputFile = filepath.Join(filepath.Dir(outputFile), filepath.Base(inputFile)) + ".pcv"
|
||||
}
|
||||
}
|
||||
}),
|
||||
giu.Checkbox("Compress files", &compress),
|
||||
giu.Tooltip("Compress files with Deflate before encrypting"),
|
||||
),
|
||||
).Build()
|
||||
|
@ -723,7 +740,7 @@ func draw() {
|
|||
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")
|
||||
f.SetInitFilename("encrypted-" + strconv.Itoa(int(time.Now().Unix())))
|
||||
}
|
||||
|
||||
// Get the chosen file path
|
||||
|
@ -740,21 +757,6 @@ func draw() {
|
|||
} else {
|
||||
file += filepath.Ext(inputFile) + ".pcv"
|
||||
}
|
||||
externalDst = false
|
||||
GOOS := strings.ToLower(runtime.GOOS)
|
||||
if strings.HasPrefix(GOOS, "windows") {
|
||||
if !strings.HasPrefix(file, "C:") {
|
||||
externalDst = true
|
||||
}
|
||||
} else if strings.HasPrefix(GOOS, "linux") {
|
||||
if strings.Contains(file, "/media/") || strings.Contains(file, "/mnt/") {
|
||||
externalDst = true
|
||||
}
|
||||
} else if strings.HasPrefix(GOOS, "darwin") {
|
||||
if strings.Contains(file, "/Volumes/") {
|
||||
externalDst = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if strings.HasSuffix(inputFile, ".zip.pcv") {
|
||||
file += ".zip"
|
||||
|
@ -787,20 +789,12 @@ func draw() {
|
|||
).Build()
|
||||
return
|
||||
}
|
||||
if temporaryZip && externalDst {
|
||||
giu.Style().SetColor(giu.StyleColorText, YELLOW).To(
|
||||
giu.Label("Warning: unencrypted temp files will be created"),
|
||||
).Build()
|
||||
} else if temporaryZip {
|
||||
if requiredFreeSpace > 0 {
|
||||
giu.Style().SetColor(giu.StyleColorText, WHITE).To(
|
||||
giu.Label("Ready (info: will create a temporary zip file)"),
|
||||
).Build()
|
||||
} else if externalDst {
|
||||
giu.Style().SetColor(giu.StyleColorText, WHITE).To(
|
||||
giu.Label("Ready (info: target may be an external drive)"),
|
||||
giu.Label("Ready (ensure " + sizeify(requiredFreeSpace) + " of disk space is free)"),
|
||||
).Build()
|
||||
} else {
|
||||
giu.Style().SetColor(giu.StyleColorText, mainStatusColor).To(
|
||||
giu.Style().SetColor(giu.StyleColorText, WHITE).To(
|
||||
giu.Label("Ready"),
|
||||
).Build()
|
||||
}
|
||||
|
@ -871,12 +865,18 @@ func onDrop(names []string) {
|
|||
folders++
|
||||
mode = "encrypt"
|
||||
inputLabel = "1 folder"
|
||||
startLabel = "Encrypt"
|
||||
startLabel = "Zip and Encrypt"
|
||||
onlyFolders = append(onlyFolders, names[0])
|
||||
inputFile = filepath.Join(filepath.Dir(names[0]), "Encrypted") + ".zip"
|
||||
inputFile = filepath.Join(filepath.Dir(names[0]), "encrypted-"+strconv.Itoa(int(time.Now().Unix()))) + ".zip"
|
||||
outputFile = inputFile + ".pcv"
|
||||
size, err := dirSize(names[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
requiredFreeSpace = 2 * size
|
||||
} else { // A file was dropped
|
||||
files++
|
||||
requiredFreeSpace += stat.Size()
|
||||
|
||||
// Is the file a part of a split volume?
|
||||
nums := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
|
@ -913,6 +913,7 @@ func onDrop(names []string) {
|
|||
}
|
||||
totalFiles++
|
||||
compressTotal += stat.Size()
|
||||
requiredFreeSpace += stat.Size()
|
||||
}
|
||||
} else {
|
||||
outputFile = names[0][:len(names[0])-4]
|
||||
|
@ -1002,7 +1003,7 @@ func onDrop(names []string) {
|
|||
}
|
||||
} else { // There are multiple dropped items
|
||||
mode = "encrypt"
|
||||
startLabel = "Encrypt"
|
||||
startLabel = "Zip and Encrypt"
|
||||
|
||||
// Go through each dropped item and add to corresponding slices
|
||||
for _, name := range names {
|
||||
|
@ -1016,6 +1017,7 @@ func onDrop(names []string) {
|
|||
allFiles = append(allFiles, name)
|
||||
|
||||
compressTotal += stat.Size()
|
||||
requiredFreeSpace += 2 * stat.Size()
|
||||
inputLabel = fmt.Sprintf("Scanning files... (%s)", sizeify(compressTotal))
|
||||
giu.Update()
|
||||
}
|
||||
|
@ -1039,9 +1041,9 @@ func onDrop(names []string) {
|
|||
}
|
||||
|
||||
// Set the input and output paths
|
||||
inputFile = filepath.Join(filepath.Dir(names[0]), "Encrypted") + ".zip"
|
||||
inputFile = filepath.Join(filepath.Dir(names[0]), "encrypted-"+strconv.Itoa(int(time.Now().Unix()))) + ".zip"
|
||||
outputFile = inputFile + ".pcv"
|
||||
temporaryZip = true
|
||||
usingTempZip = true
|
||||
}
|
||||
|
||||
// Recursively add all files in 'onlyFolders' to 'allFiles'
|
||||
|
@ -1054,6 +1056,7 @@ func onDrop(names []string) {
|
|||
if err == nil && !stat.IsDir() {
|
||||
allFiles = append(allFiles, path)
|
||||
compressTotal += stat.Size()
|
||||
requiredFreeSpace += 2 * stat.Size()
|
||||
inputLabel = fmt.Sprintf("Scanning files... (%s)", sizeify(compressTotal))
|
||||
giu.Update()
|
||||
}
|
||||
|
@ -1086,6 +1089,17 @@ func work() {
|
|||
var keyfileHashRef []byte // Same as 'keyfileHash', but used for comparison
|
||||
var authTag []byte // 64-byte authentication tag (BLAKE2b or HMAC-SHA3)
|
||||
|
||||
var tempZipCipherW *chacha20.Cipher
|
||||
var tempZipCipherR *chacha20.Cipher
|
||||
var tempZipInUse bool = false
|
||||
func() {
|
||||
key, nonce := make([]byte, 32), make([]byte, 12)
|
||||
rand.Read(key)
|
||||
rand.Read(nonce)
|
||||
tempZipCipherW, _ = chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||
tempZipCipherR, _ = chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||
}()
|
||||
|
||||
// Combine/compress all files into a .zip file if needed
|
||||
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||
// Consider case where compressing only one file
|
||||
|
@ -1103,7 +1117,7 @@ func work() {
|
|||
}
|
||||
|
||||
// Open a temporary .zip for writing
|
||||
inputFile = strings.TrimSuffix(outputFile, ".pcv")
|
||||
inputFile = strings.TrimSuffix(outputFile, ".pcv") + ".tmp"
|
||||
file, err := os.Create(inputFile)
|
||||
if err != nil { // Make sure file is writable
|
||||
accessDenied("Write")
|
||||
|
@ -1111,7 +1125,12 @@ func work() {
|
|||
}
|
||||
|
||||
// Add each file to the .zip
|
||||
writer := zip.NewWriter(file)
|
||||
tempZip := encryptedZipWriter{
|
||||
_w: file,
|
||||
_cipher: tempZipCipherW,
|
||||
}
|
||||
tempZipInUse = true
|
||||
writer := zip.NewWriter(&tempZip)
|
||||
compressStart = time.Now()
|
||||
for i, path := range files {
|
||||
progressInfo = fmt.Sprintf("%d/%d", i+1, len(files))
|
||||
|
@ -1371,7 +1390,7 @@ func work() {
|
|||
}
|
||||
|
||||
// Create the output file
|
||||
fout, err = os.Create(outputFile)
|
||||
fout, err = os.Create(outputFile + ".incomplete")
|
||||
if err != nil {
|
||||
fin.Close()
|
||||
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||
|
@ -1456,7 +1475,7 @@ func work() {
|
|||
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||
os.Remove(inputFile)
|
||||
}
|
||||
os.Remove(outputFile)
|
||||
os.Remove(fout.Name())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1688,7 +1707,7 @@ func work() {
|
|||
}
|
||||
|
||||
// Create the output file for decryption
|
||||
fout, err = os.Create(outputFile)
|
||||
fout, err = os.Create(outputFile + ".incomplete")
|
||||
if err != nil {
|
||||
fin.Close()
|
||||
if recombine {
|
||||
|
@ -1744,13 +1763,17 @@ func work() {
|
|||
// Start the main encryption process
|
||||
canCancel = true
|
||||
startTime := time.Now()
|
||||
tempZip := encryptedZipReader{
|
||||
_r: fin,
|
||||
_cipher: tempZipCipherR,
|
||||
}
|
||||
for {
|
||||
if !working {
|
||||
cancel(fin, fout)
|
||||
if recombine || len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||
os.Remove(inputFile)
|
||||
}
|
||||
os.Remove(outputFile)
|
||||
os.Remove(fout.Name())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1761,7 +1784,13 @@ func work() {
|
|||
} else {
|
||||
src = make([]byte, MiB)
|
||||
}
|
||||
size, err := fin.Read(src)
|
||||
|
||||
var size int
|
||||
if tempZipInUse {
|
||||
size, err = tempZip.Read(src)
|
||||
} else {
|
||||
size, err = fin.Read(src)
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
@ -1881,7 +1910,7 @@ func work() {
|
|||
if recombine || len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||
os.Remove(inputFile)
|
||||
}
|
||||
os.Remove(outputFile)
|
||||
os.Remove(fout.Name())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1960,6 +1989,8 @@ func work() {
|
|||
fin.Close()
|
||||
fout.Close()
|
||||
|
||||
os.Rename(outputFile+".incomplete", outputFile)
|
||||
|
||||
// Add plausible deniability
|
||||
if mode == "encrypt" && deniability {
|
||||
popupStatus = "Adding plausible deniability..."
|
||||
|
@ -1967,13 +1998,13 @@ func work() {
|
|||
giu.Update()
|
||||
|
||||
// Get size of volume for showing progress
|
||||
stat, _ := os.Stat(fout.Name())
|
||||
stat, _ := os.Stat(outputFile)
|
||||
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())
|
||||
os.Rename(outputFile, outputFile+".tmp")
|
||||
fin, _ := os.Open(outputFile + ".tmp")
|
||||
fout, _ := os.Create(outputFile + ".incomplete")
|
||||
|
||||
// Use a random Argon2 salt and XChaCha20 nonce
|
||||
salt := make([]byte, 16)
|
||||
|
@ -2023,6 +2054,7 @@ func work() {
|
|||
fin.Close()
|
||||
fout.Close()
|
||||
os.Remove(fin.Name())
|
||||
os.Rename(outputFile+".incomplete", outputFile)
|
||||
canCancel = true
|
||||
giu.Update()
|
||||
}
|
||||
|
@ -2067,7 +2099,7 @@ func work() {
|
|||
startTime := time.Now()
|
||||
for i := 0; i < chunks; i++ {
|
||||
// Make the chunk
|
||||
fout, _ := os.Create(fmt.Sprintf("%s.%d", outputFile, i))
|
||||
fout, _ := os.Create(fmt.Sprintf("%s.%d.incomplete", outputFile, i))
|
||||
done := 0
|
||||
|
||||
// Copy data into the chunk
|
||||
|
@ -2133,6 +2165,10 @@ func work() {
|
|||
|
||||
fin.Close()
|
||||
os.Remove(outputFile)
|
||||
names, _ = filepath.Glob(outputFile + ".*.incomplete")
|
||||
for _, i := range names {
|
||||
os.Rename(i, strings.TrimSuffix(i, ".incomplete"))
|
||||
}
|
||||
}
|
||||
|
||||
canCancel = false
|
||||
|
@ -2304,8 +2340,8 @@ func resetUI() {
|
|||
mainStatus = "Ready"
|
||||
mainStatusColor = WHITE
|
||||
popupStatus = ""
|
||||
temporaryZip = false
|
||||
externalDst = false
|
||||
usingTempZip = false
|
||||
requiredFreeSpace = 0
|
||||
|
||||
progress = 0
|
||||
progressInfo = ""
|
||||
|
@ -2450,7 +2486,7 @@ func unpackArchive(zipPath string) error {
|
|||
|
||||
// Make directory if current entry is a folder
|
||||
if f.FileInfo().IsDir() {
|
||||
if err := os.MkdirAll(outPath, f.Mode()); err != nil {
|
||||
if err := os.MkdirAll(outPath, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -2469,7 +2505,7 @@ func unpackArchive(zipPath string) error {
|
|||
outPath := filepath.Join(extractDir, f.Name)
|
||||
|
||||
// Otherwise create necessary parent directories
|
||||
if err := os.MkdirAll(filepath.Dir(outPath), 0755); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(outPath), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -2517,6 +2553,20 @@ func unpackArchive(zipPath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func dirSize(path string) (int64, error) {
|
||||
var size int64
|
||||
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
size += info.Size()
|
||||
}
|
||||
return err
|
||||
})
|
||||
return size, err
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create the main window
|
||||
window = giu.NewMasterWindow("Picocrypt "+version[1:], 318, 507, giu.MasterWindowFlagsNotResizable)
|
||||
|
|
Loading…
Add table
Reference in a new issue