mirror of
https://github.com/Picocrypt/Picocrypt.git
synced 2025-05-12 05:48:30 +02:00
Merge 9b2b69e442
into f0bfe3ba03
This commit is contained in:
commit
504e908195
9 changed files with 306 additions and 165 deletions
24
.github/ISSUE_TEMPLATE/default.yml
vendored
24
.github/ISSUE_TEMPLATE/default.yml
vendored
|
@ -12,11 +12,11 @@ body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
|
@ -24,7 +24,7 @@ body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
|
@ -32,7 +32,7 @@ body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
|
@ -40,7 +40,7 @@ body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
|
@ -48,7 +48,7 @@ body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
|
@ -56,7 +56,7 @@ body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
|
@ -64,7 +64,7 @@ body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
|
@ -72,8 +72,9 @@ body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
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
|
- type: checkboxes
|
||||||
|
id: confirmation
|
||||||
attributes:
|
attributes:
|
||||||
label: "Please confirm:"
|
label: "Please confirm:"
|
||||||
options:
|
options:
|
||||||
|
@ -83,7 +84,12 @@ body:
|
||||||
required: true
|
required: true
|
||||||
- label: "I acknowledge my issue may be ignored or closed without explanation"
|
- label: "I acknowledge my issue may be ignored or closed without explanation"
|
||||||
required: true
|
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
|
- type: input
|
||||||
|
id: summary
|
||||||
attributes:
|
attributes:
|
||||||
label: "Describe the issue briefly in a few sentences:"
|
label: "Describe the issue briefly in a few sentences:"
|
||||||
description: "You can add more details in a separate comment after creating the issue."
|
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 }}
|
13
Changelog.md
13
Changelog.md
|
@ -1,6 +1,17 @@
|
||||||
# Future
|
# Future
|
||||||
<ul>
|
<ul>
|
||||||
<li>Migrate golang.org/x/crypto to standard library imports (https://github.com/golang/go/issues/65269)</li>
|
<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>✓ 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>
|
</ul>
|
||||||
|
|
||||||
# v1.47 (Released 02/19/2025)
|
# v1.47 (Released 02/19/2025)
|
||||||
|
|
|
@ -18,8 +18,6 @@ Picocrypt is a very small (hence <i>Pico</i>), very simple, yet very secure encr
|
||||||
## Windows
|
## Windows
|
||||||
Picocrypt for Windows is as simple as it gets. To download the latest, standalone, and portable executable for Windows, click <a href="https://github.com/Picocrypt/Picocrypt/releases/latest/download/Picocrypt.exe">here</a>. If Microsoft Defender or your antivirus flags Picocrypt as a virus, please do your part and submit it as a false positive for the betterment of everyone.
|
Picocrypt for Windows is as simple as it gets. To download the latest, standalone, and portable executable for Windows, click <a href="https://github.com/Picocrypt/Picocrypt/releases/latest/download/Picocrypt.exe">here</a>. If Microsoft Defender or your antivirus flags Picocrypt as a virus, please do your part and submit it as a false positive for the betterment of everyone.
|
||||||
|
|
||||||
If you use Picocrypt frequently, you can download an installer <a href="https://github.com/Picocrypt/Picocrypt/releases/download/1.47/Install-Picocrypt.exe">here</a> for easier launching. It does not require any admin permissions to install and it also bundles a software OpenGL renderer for compatibility, so if the portable executable isn't working, this installer likely will.
|
|
||||||
|
|
||||||
## macOS
|
## macOS
|
||||||
Picocrypt for macOS is very simple as well. Download Picocrypt <a href="https://github.com/Picocrypt/Picocrypt/releases/latest/download/Picocrypt.dmg">here</a>, open the container, and drag Picocrypt to your Applications. You may need to manually trust the app from a terminal and control-click on the app if macOS prevents you from opening it:
|
Picocrypt for macOS is very simple as well. Download Picocrypt <a href="https://github.com/Picocrypt/Picocrypt/releases/latest/download/Picocrypt.dmg">here</a>, open the container, and drag Picocrypt to your Applications. You may need to manually trust the app from a terminal and control-click on the app if macOS prevents you from opening it:
|
||||||
```
|
```
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.47
|
1.48
|
6
dist/windows/versioninfo.rc
vendored
6
dist/windows/versioninfo.rc
vendored
|
@ -1,6 +1,6 @@
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 1,47,0,0
|
FILEVERSION 1,48,0,0
|
||||||
PRODUCTVERSION 1,47,0,0
|
PRODUCTVERSION 1,48,0,0
|
||||||
FILEOS 0x40004
|
FILEOS 0x40004
|
||||||
FILETYPE 0x1
|
FILETYPE 0x1
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ BLOCK "StringFileInfo"
|
||||||
{
|
{
|
||||||
BLOCK "040904B0"
|
BLOCK "040904B0"
|
||||||
{
|
{
|
||||||
VALUE "FileVersion", "1.47"
|
VALUE "FileVersion", "1.48"
|
||||||
VALUE "LegalCopyright", "\xA9 Evan Su & contributors, GPLv3"
|
VALUE "LegalCopyright", "\xA9 Evan Su & contributors, GPLv3"
|
||||||
VALUE "ProductName", "Picocrypt"
|
VALUE "ProductName", "Picocrypt"
|
||||||
}
|
}
|
||||||
|
|
361
src/Picocrypt.go
361
src/Picocrypt.go
|
@ -2,7 +2,7 @@ package main
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Picocrypt v1.47
|
Picocrypt v1.48
|
||||||
Copyright (c) Evan Su
|
Copyright (c) Evan Su
|
||||||
Released under a GNU GPL v3 License
|
Released under a GNU GPL v3 License
|
||||||
https://github.com/Picocrypt/Picocrypt
|
https://github.com/Picocrypt/Picocrypt
|
||||||
|
@ -60,7 +60,7 @@ var TRANSPARENT = color.RGBA{0x00, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
// Generic variables
|
// Generic variables
|
||||||
var window *giu.MasterWindow
|
var window *giu.MasterWindow
|
||||||
var version = "v1.47"
|
var version = "v1.48"
|
||||||
var dpi float32
|
var dpi float32
|
||||||
var mode string
|
var mode string
|
||||||
var working bool
|
var working bool
|
||||||
|
@ -130,6 +130,8 @@ var startLabel = "Start"
|
||||||
var mainStatus = "Ready"
|
var mainStatus = "Ready"
|
||||||
var mainStatusColor = WHITE
|
var mainStatusColor = WHITE
|
||||||
var popupStatus string
|
var popupStatus string
|
||||||
|
var usingTempZip bool
|
||||||
|
var requiredFreeSpace int64
|
||||||
|
|
||||||
// Progress variables
|
// Progress variables
|
||||||
var progress float32
|
var progress float32
|
||||||
|
@ -173,10 +175,144 @@ func (p *compressorProgress) Read(data []byte) (int, error) {
|
||||||
return read, err
|
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) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyfile && keyfiles == nil {
|
||||||
|
mainStatus = "Please select your keyfiles"
|
||||||
|
mainStatusColor = RED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmp, err := strconv.Atoi(splitSize)
|
||||||
|
if split && (splitSize == "" || tmp <= 0 || err != nil) {
|
||||||
|
mainStatus = "Invalid chunk size"
|
||||||
|
mainStatusColor = RED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if output file already exists
|
||||||
|
_, err = os.Stat(outputFile)
|
||||||
|
|
||||||
|
// Check if any split chunks already exist
|
||||||
|
if split {
|
||||||
|
names, _ := filepath.Glob(outputFile + ".*")
|
||||||
|
if len(names) > 0 {
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
err = os.ErrNotExist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If files already exist, show the overwrite modal
|
||||||
|
if err == nil && !recursively {
|
||||||
|
showOverwrite = true
|
||||||
|
modalId++
|
||||||
|
giu.Update()
|
||||||
|
} else { // Nothing to worry about, start working
|
||||||
|
showProgress = true
|
||||||
|
fastDecode = true
|
||||||
|
canCancel = true
|
||||||
|
modalId++
|
||||||
|
giu.Update()
|
||||||
|
if !recursively {
|
||||||
|
go func() {
|
||||||
|
work()
|
||||||
|
working = false
|
||||||
|
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()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The main user interface
|
// The main user interface
|
||||||
func draw() {
|
func draw() {
|
||||||
giu.SingleWindow().Flags(524351).Layout(
|
giu.SingleWindow().Flags(524351).Layout(
|
||||||
giu.Custom(func() {
|
giu.Custom(func() {
|
||||||
|
if giu.IsKeyReleased(giu.KeyEnter) {
|
||||||
|
onClickStartButton()
|
||||||
|
return
|
||||||
|
}
|
||||||
if showPassgen {
|
if showPassgen {
|
||||||
giu.PopupModal("Generate password:##"+strconv.Itoa(modalId)).Flags(6).Layout(
|
giu.PopupModal("Generate password:##"+strconv.Itoa(modalId)).Flags(6).Layout(
|
||||||
giu.Row(
|
giu.Row(
|
||||||
|
@ -499,16 +635,8 @@ 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.Style().SetDisabled(recursively).To(
|
giu.Style().SetDisabled(recursively || !(len(allFiles) > 1 || len(onlyFolders) > 0)).To(
|
||||||
giu.Checkbox("Compress files", &compress).OnChange(func() {
|
giu.Checkbox("Compress files", &compress),
|
||||||
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.Tooltip("Compress files with Deflate before encrypting"),
|
giu.Tooltip("Compress files with Deflate before encrypting"),
|
||||||
),
|
),
|
||||||
).Build()
|
).Build()
|
||||||
|
@ -612,7 +740,7 @@ func draw() {
|
||||||
tmp := strings.TrimSuffix(filepath.Base(outputFile), ".pcv")
|
tmp := strings.TrimSuffix(filepath.Base(outputFile), ".pcv")
|
||||||
f.SetInitFilename(strings.TrimSuffix(tmp, filepath.Ext(tmp)))
|
f.SetInitFilename(strings.TrimSuffix(tmp, filepath.Ext(tmp)))
|
||||||
if mode == "encrypt" && (len(allFiles) > 1 || len(onlyFolders) > 0 || compress) {
|
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
|
// Get the chosen file path
|
||||||
|
@ -653,106 +781,24 @@ func draw() {
|
||||||
return startLabel
|
return startLabel
|
||||||
}
|
}
|
||||||
return "Process"
|
return "Process"
|
||||||
}()).Size(giu.Auto, 34).OnClick(func() {
|
}()).Size(giu.Auto, 34).OnClick(onClickStartButton),
|
||||||
if keyfile && keyfiles == nil {
|
giu.Custom(func() {
|
||||||
mainStatus = "Please select your keyfiles"
|
if mainStatus != "Ready" {
|
||||||
mainStatusColor = RED
|
giu.Style().SetColor(giu.StyleColorText, mainStatusColor).To(
|
||||||
|
giu.Label(mainStatus),
|
||||||
|
).Build()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tmp, err := strconv.Atoi(splitSize)
|
if requiredFreeSpace > 0 {
|
||||||
if split && (splitSize == "" || tmp <= 0 || err != nil) {
|
giu.Style().SetColor(giu.StyleColorText, WHITE).To(
|
||||||
mainStatus = "Invalid chunk size"
|
giu.Label("Ready (ensure " + sizeify(requiredFreeSpace) + " of disk space is free)"),
|
||||||
mainStatusColor = RED
|
).Build()
|
||||||
return
|
} else {
|
||||||
}
|
giu.Style().SetColor(giu.StyleColorText, WHITE).To(
|
||||||
|
giu.Label("Ready"),
|
||||||
// Check if output file already exists
|
).Build()
|
||||||
_, err = os.Stat(outputFile)
|
|
||||||
|
|
||||||
// Check if any split chunks already exist
|
|
||||||
if split {
|
|
||||||
names, _ := filepath.Glob(outputFile + ".*")
|
|
||||||
if len(names) > 0 {
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
err = os.ErrNotExist
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If files already exist, show the overwrite modal
|
|
||||||
if err == nil && !recursively {
|
|
||||||
showOverwrite = true
|
|
||||||
modalId++
|
|
||||||
giu.Update()
|
|
||||||
} else { // Nothing to worry about, start working
|
|
||||||
showProgress = true
|
|
||||||
fastDecode = true
|
|
||||||
canCancel = true
|
|
||||||
modalId++
|
|
||||||
giu.Update()
|
|
||||||
if !recursively {
|
|
||||||
go func() {
|
|
||||||
work()
|
|
||||||
working = false
|
|
||||||
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.Label(mainStatus),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
giu.Custom(func() {
|
giu.Custom(func() {
|
||||||
|
@ -819,12 +865,18 @@ func onDrop(names []string) {
|
||||||
folders++
|
folders++
|
||||||
mode = "encrypt"
|
mode = "encrypt"
|
||||||
inputLabel = "1 folder"
|
inputLabel = "1 folder"
|
||||||
startLabel = "Encrypt"
|
startLabel = "Zip and Encrypt"
|
||||||
onlyFolders = append(onlyFolders, names[0])
|
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"
|
outputFile = inputFile + ".pcv"
|
||||||
|
size, err := dirSize(names[0])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
requiredFreeSpace = 2 * size
|
||||||
} else { // A file was dropped
|
} else { // A file was dropped
|
||||||
files++
|
files++
|
||||||
|
requiredFreeSpace += stat.Size()
|
||||||
|
|
||||||
// Is the file a part of a split volume?
|
// Is the file a part of a split volume?
|
||||||
nums := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
nums := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||||
|
@ -861,6 +913,7 @@ func onDrop(names []string) {
|
||||||
}
|
}
|
||||||
totalFiles++
|
totalFiles++
|
||||||
compressTotal += stat.Size()
|
compressTotal += stat.Size()
|
||||||
|
requiredFreeSpace += stat.Size()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outputFile = names[0][:len(names[0])-4]
|
outputFile = names[0][:len(names[0])-4]
|
||||||
|
@ -950,7 +1003,7 @@ func onDrop(names []string) {
|
||||||
}
|
}
|
||||||
} else { // There are multiple dropped items
|
} else { // There are multiple dropped items
|
||||||
mode = "encrypt"
|
mode = "encrypt"
|
||||||
startLabel = "Encrypt"
|
startLabel = "Zip and Encrypt"
|
||||||
|
|
||||||
// Go through each dropped item and add to corresponding slices
|
// Go through each dropped item and add to corresponding slices
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
@ -964,6 +1017,7 @@ func onDrop(names []string) {
|
||||||
allFiles = append(allFiles, name)
|
allFiles = append(allFiles, name)
|
||||||
|
|
||||||
compressTotal += stat.Size()
|
compressTotal += stat.Size()
|
||||||
|
requiredFreeSpace += 2 * stat.Size()
|
||||||
inputLabel = fmt.Sprintf("Scanning files... (%s)", sizeify(compressTotal))
|
inputLabel = fmt.Sprintf("Scanning files... (%s)", sizeify(compressTotal))
|
||||||
giu.Update()
|
giu.Update()
|
||||||
}
|
}
|
||||||
|
@ -987,8 +1041,9 @@ func onDrop(names []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the input and output paths
|
// 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"
|
outputFile = inputFile + ".pcv"
|
||||||
|
usingTempZip = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively add all files in 'onlyFolders' to 'allFiles'
|
// Recursively add all files in 'onlyFolders' to 'allFiles'
|
||||||
|
@ -1001,6 +1056,7 @@ func onDrop(names []string) {
|
||||||
if err == nil && !stat.IsDir() {
|
if err == nil && !stat.IsDir() {
|
||||||
allFiles = append(allFiles, path)
|
allFiles = append(allFiles, path)
|
||||||
compressTotal += stat.Size()
|
compressTotal += stat.Size()
|
||||||
|
requiredFreeSpace += 2 * stat.Size()
|
||||||
inputLabel = fmt.Sprintf("Scanning files... (%s)", sizeify(compressTotal))
|
inputLabel = fmt.Sprintf("Scanning files... (%s)", sizeify(compressTotal))
|
||||||
giu.Update()
|
giu.Update()
|
||||||
}
|
}
|
||||||
|
@ -1033,6 +1089,17 @@ func work() {
|
||||||
var keyfileHashRef []byte // Same as 'keyfileHash', but used for comparison
|
var keyfileHashRef []byte // Same as 'keyfileHash', but used for comparison
|
||||||
var authTag []byte // 64-byte authentication tag (BLAKE2b or HMAC-SHA3)
|
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
|
// Combine/compress all files into a .zip file if needed
|
||||||
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||||
// Consider case where compressing only one file
|
// Consider case where compressing only one file
|
||||||
|
@ -1050,7 +1117,7 @@ func work() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open a temporary .zip for writing
|
// Open a temporary .zip for writing
|
||||||
inputFile = strings.TrimSuffix(outputFile, ".pcv")
|
inputFile = strings.TrimSuffix(outputFile, ".pcv") + ".tmp"
|
||||||
file, err := os.Create(inputFile)
|
file, err := os.Create(inputFile)
|
||||||
if err != nil { // Make sure file is writable
|
if err != nil { // Make sure file is writable
|
||||||
accessDenied("Write")
|
accessDenied("Write")
|
||||||
|
@ -1058,7 +1125,12 @@ func work() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add each file to the .zip
|
// 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()
|
compressStart = time.Now()
|
||||||
for i, path := range files {
|
for i, path := range files {
|
||||||
progressInfo = fmt.Sprintf("%d/%d", i+1, len(files))
|
progressInfo = fmt.Sprintf("%d/%d", i+1, len(files))
|
||||||
|
@ -1318,7 +1390,7 @@ func work() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the output file
|
// Create the output file
|
||||||
fout, err = os.Create(outputFile)
|
fout, err = os.Create(outputFile + ".incomplete")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fin.Close()
|
fin.Close()
|
||||||
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||||
|
@ -1403,7 +1475,7 @@ func work() {
|
||||||
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
if len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||||
os.Remove(inputFile)
|
os.Remove(inputFile)
|
||||||
}
|
}
|
||||||
os.Remove(outputFile)
|
os.Remove(fout.Name())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1635,7 +1707,7 @@ func work() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the output file for decryption
|
// Create the output file for decryption
|
||||||
fout, err = os.Create(outputFile)
|
fout, err = os.Create(outputFile + ".incomplete")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fin.Close()
|
fin.Close()
|
||||||
if recombine {
|
if recombine {
|
||||||
|
@ -1691,13 +1763,17 @@ func work() {
|
||||||
// Start the main encryption process
|
// Start the main encryption process
|
||||||
canCancel = true
|
canCancel = true
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
tempZip := encryptedZipReader{
|
||||||
|
_r: fin,
|
||||||
|
_cipher: tempZipCipherR,
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
if !working {
|
if !working {
|
||||||
cancel(fin, fout)
|
cancel(fin, fout)
|
||||||
if recombine || len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
if recombine || len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||||
os.Remove(inputFile)
|
os.Remove(inputFile)
|
||||||
}
|
}
|
||||||
os.Remove(outputFile)
|
os.Remove(fout.Name())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1708,7 +1784,13 @@ func work() {
|
||||||
} else {
|
} else {
|
||||||
src = make([]byte, MiB)
|
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 {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1828,7 +1910,7 @@ func work() {
|
||||||
if recombine || len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
if recombine || len(allFiles) > 1 || len(onlyFolders) > 0 || compress {
|
||||||
os.Remove(inputFile)
|
os.Remove(inputFile)
|
||||||
}
|
}
|
||||||
os.Remove(outputFile)
|
os.Remove(fout.Name())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1907,6 +1989,8 @@ func work() {
|
||||||
fin.Close()
|
fin.Close()
|
||||||
fout.Close()
|
fout.Close()
|
||||||
|
|
||||||
|
os.Rename(outputFile+".incomplete", outputFile)
|
||||||
|
|
||||||
// Add plausible deniability
|
// Add plausible deniability
|
||||||
if mode == "encrypt" && deniability {
|
if mode == "encrypt" && deniability {
|
||||||
popupStatus = "Adding plausible deniability..."
|
popupStatus = "Adding plausible deniability..."
|
||||||
|
@ -1914,13 +1998,13 @@ func work() {
|
||||||
giu.Update()
|
giu.Update()
|
||||||
|
|
||||||
// Get size of volume for showing progress
|
// Get size of volume for showing progress
|
||||||
stat, _ := os.Stat(fout.Name())
|
stat, _ := os.Stat(outputFile)
|
||||||
total := stat.Size()
|
total := stat.Size()
|
||||||
|
|
||||||
// Rename the output volume to free up the filename
|
// Rename the output volume to free up the filename
|
||||||
os.Rename(fout.Name(), fout.Name()+".tmp")
|
os.Rename(outputFile, outputFile+".tmp")
|
||||||
fin, _ := os.Open(fout.Name() + ".tmp")
|
fin, _ := os.Open(outputFile + ".tmp")
|
||||||
fout, _ := os.Create(fout.Name())
|
fout, _ := os.Create(outputFile + ".incomplete")
|
||||||
|
|
||||||
// Use a random Argon2 salt and XChaCha20 nonce
|
// Use a random Argon2 salt and XChaCha20 nonce
|
||||||
salt := make([]byte, 16)
|
salt := make([]byte, 16)
|
||||||
|
@ -1970,6 +2054,7 @@ func work() {
|
||||||
fin.Close()
|
fin.Close()
|
||||||
fout.Close()
|
fout.Close()
|
||||||
os.Remove(fin.Name())
|
os.Remove(fin.Name())
|
||||||
|
os.Rename(outputFile+".incomplete", outputFile)
|
||||||
canCancel = true
|
canCancel = true
|
||||||
giu.Update()
|
giu.Update()
|
||||||
}
|
}
|
||||||
|
@ -2014,7 +2099,7 @@ func work() {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
for i := 0; i < chunks; i++ {
|
for i := 0; i < chunks; i++ {
|
||||||
// Make the chunk
|
// Make the chunk
|
||||||
fout, _ := os.Create(fmt.Sprintf("%s.%d", outputFile, i))
|
fout, _ := os.Create(fmt.Sprintf("%s.%d.incomplete", outputFile, i))
|
||||||
done := 0
|
done := 0
|
||||||
|
|
||||||
// Copy data into the chunk
|
// Copy data into the chunk
|
||||||
|
@ -2080,6 +2165,10 @@ func work() {
|
||||||
|
|
||||||
fin.Close()
|
fin.Close()
|
||||||
os.Remove(outputFile)
|
os.Remove(outputFile)
|
||||||
|
names, _ = filepath.Glob(outputFile + ".*.incomplete")
|
||||||
|
for _, i := range names {
|
||||||
|
os.Rename(i, strings.TrimSuffix(i, ".incomplete"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canCancel = false
|
canCancel = false
|
||||||
|
@ -2251,6 +2340,8 @@ func resetUI() {
|
||||||
mainStatus = "Ready"
|
mainStatus = "Ready"
|
||||||
mainStatusColor = WHITE
|
mainStatusColor = WHITE
|
||||||
popupStatus = ""
|
popupStatus = ""
|
||||||
|
usingTempZip = false
|
||||||
|
requiredFreeSpace = 0
|
||||||
|
|
||||||
progress = 0
|
progress = 0
|
||||||
progressInfo = ""
|
progressInfo = ""
|
||||||
|
@ -2395,7 +2486,7 @@ func unpackArchive(zipPath string) error {
|
||||||
|
|
||||||
// Make directory if current entry is a folder
|
// Make directory if current entry is a folder
|
||||||
if f.FileInfo().IsDir() {
|
if f.FileInfo().IsDir() {
|
||||||
if err := os.MkdirAll(outPath, f.Mode()); err != nil {
|
if err := os.MkdirAll(outPath, 0700); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2414,7 +2505,7 @@ func unpackArchive(zipPath string) error {
|
||||||
outPath := filepath.Join(extractDir, f.Name)
|
outPath := filepath.Join(extractDir, f.Name)
|
||||||
|
|
||||||
// Otherwise create necessary parent directories
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2462,6 +2553,20 @@ func unpackArchive(zipPath string) error {
|
||||||
return nil
|
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() {
|
func main() {
|
||||||
// Create the main window
|
// Create the main window
|
||||||
window = giu.NewMasterWindow("Picocrypt "+version[1:], 318, 507, giu.MasterWindowFlagsNotResizable)
|
window = giu.NewMasterWindow("Picocrypt "+version[1:], 318, 507, giu.MasterWindowFlagsNotResizable)
|
||||||
|
|
14
src/go.mod
14
src/go.mod
|
@ -3,18 +3,18 @@ module Picocrypt
|
||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Picocrypt/dialog v0.0.0-20240831001746-9ca708a9cd29
|
github.com/Picocrypt/dialog v0.0.0-20250412233924-78f7b909315b
|
||||||
github.com/Picocrypt/giu v0.0.0-20240831005244-5771b35043ac
|
github.com/Picocrypt/giu v0.0.0-20250412235908-fe90a482e6f2
|
||||||
github.com/Picocrypt/imgui-go v0.0.0-20240831004007-6f60d7beadf6
|
github.com/Picocrypt/imgui-go v0.0.0-20250412235405-d86b230f5fbb
|
||||||
github.com/Picocrypt/infectious v0.0.0-20240830233326-3a050f65f9ec
|
github.com/Picocrypt/infectious v0.0.0-20250412183341-9f88c6307b39
|
||||||
github.com/Picocrypt/serpent v0.0.0-20240830233833-9ad6ab254fd7
|
github.com/Picocrypt/serpent v0.0.0-20240830233833-9ad6ab254fd7
|
||||||
github.com/Picocrypt/zxcvbn-go v0.0.0-20240831000415-fccb38ccb913
|
github.com/Picocrypt/zxcvbn-go v0.0.0-20250412183938-d59695960527
|
||||||
golang.org/x/crypto v0.37.0
|
golang.org/x/crypto v0.37.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Picocrypt/gl v0.0.0-20240831002619-6531d2bba5fc // indirect
|
github.com/Picocrypt/gl v0.0.0-20250412234430-767b58dbf936 // indirect
|
||||||
github.com/Picocrypt/glfw/v3.3/glfw v0.0.0-20240831003212-7f16c5fb374b // indirect
|
github.com/Picocrypt/glfw/v3.3/glfw v0.0.0-20250412234750-7b96bfdb8dd8 // indirect
|
||||||
github.com/Picocrypt/mainthread v0.0.0-20240831004314-496f638392b3 // indirect
|
github.com/Picocrypt/mainthread v0.0.0-20240831004314-496f638392b3 // indirect
|
||||||
github.com/Picocrypt/w32 v0.0.0-20240831001500-1183079d4d57 // indirect
|
github.com/Picocrypt/w32 v0.0.0-20240831001500-1183079d4d57 // indirect
|
||||||
golang.org/x/sys v0.32.0 // indirect
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
|
|
28
src/go.sum
28
src/go.sum
|
@ -1,23 +1,23 @@
|
||||||
github.com/Picocrypt/dialog v0.0.0-20240831001746-9ca708a9cd29 h1:WIgRST/mpLiBEG2MF5MRPBDYYevLw7y14cwUEDjG5+Q=
|
github.com/Picocrypt/dialog v0.0.0-20250412233924-78f7b909315b h1:k5YGEx61N6K8l2l6AQ1u5W2aR+47sVZWFyqXS/f5lIA=
|
||||||
github.com/Picocrypt/dialog v0.0.0-20240831001746-9ca708a9cd29/go.mod h1:raXVkdcX4495+fW9Ac+kvPMHRNk0rOcNXEWFD71B2As=
|
github.com/Picocrypt/dialog v0.0.0-20250412233924-78f7b909315b/go.mod h1:OyaP0Tz19qL3RAGq5Ntues+WVrIbHh5MrfqoA/qhqeg=
|
||||||
github.com/Picocrypt/giu v0.0.0-20240831005244-5771b35043ac h1:Z21enGbi450NyI7UZSoEuu//axifyGl63BJVjHX3ZXc=
|
github.com/Picocrypt/giu v0.0.0-20250412235908-fe90a482e6f2 h1:SPR2efZTpZJON/2mNifLi68Gl9Epxh/1nXb3kGGHCcg=
|
||||||
github.com/Picocrypt/giu v0.0.0-20240831005244-5771b35043ac/go.mod h1:x7jbmZVofU9rn5WJj2+riU85Zo0MFlfp1sMTnKFQhc0=
|
github.com/Picocrypt/giu v0.0.0-20250412235908-fe90a482e6f2/go.mod h1:jd6AonK0ZI02R7GqLWb4gWJz/A2ClF36Y4fFMR8Lzbk=
|
||||||
github.com/Picocrypt/gl v0.0.0-20240831002619-6531d2bba5fc h1:5ckKMFhiz/Af6+sdkGlw74BU+rKRmoFWqU/rXHGUe3g=
|
github.com/Picocrypt/gl v0.0.0-20250412234430-767b58dbf936 h1:6MChjQ4AZC2ISBjbgZU/z6tSUxYP50NkRvAu0T2kjlY=
|
||||||
github.com/Picocrypt/gl v0.0.0-20240831002619-6531d2bba5fc/go.mod h1:VknKAZzEoKP9nqrc/dveCwR5L01B9V8yLqtpvYmQ3DA=
|
github.com/Picocrypt/gl v0.0.0-20250412234430-767b58dbf936/go.mod h1:pMdf3io/y3I+zYZ6/xFb3MlI2AgL38enDDIKuR0n2qA=
|
||||||
github.com/Picocrypt/glfw/v3.3/glfw v0.0.0-20240831003212-7f16c5fb374b h1:hSaQU4P9KbMg9s2Jp2mTk9G5G+zkf4Yse5YRoxWTDTk=
|
github.com/Picocrypt/glfw/v3.3/glfw v0.0.0-20250412234750-7b96bfdb8dd8 h1:i8wXJhSYIJTXb6sqBS6JZW7QosI9u8Ysy1BHZCTuZEc=
|
||||||
github.com/Picocrypt/glfw/v3.3/glfw v0.0.0-20240831003212-7f16c5fb374b/go.mod h1:r5awTCSm/ugmTRKmT8Hr0T4xGPI6K35eFK0s3jYCW+s=
|
github.com/Picocrypt/glfw/v3.3/glfw v0.0.0-20250412234750-7b96bfdb8dd8/go.mod h1:cX5N2TrX03DC5i5eplxopglDue/vHDs+6Ng9G9uItaI=
|
||||||
github.com/Picocrypt/imgui-go v0.0.0-20240831004007-6f60d7beadf6 h1:Y6SuxbSkQSU1hdEOpoMvp6Akq3RVX6KP1U4pKjGv3qo=
|
github.com/Picocrypt/imgui-go v0.0.0-20250412235405-d86b230f5fbb h1:0XMtv2CXx3QvC9ikeH43fJl6ql8j5EsnaiOqhsToFnY=
|
||||||
github.com/Picocrypt/imgui-go v0.0.0-20240831004007-6f60d7beadf6/go.mod h1:mGfOCkgyafVMIs1tU70va3lFSh6hSb+Vq4paVLX1Fjg=
|
github.com/Picocrypt/imgui-go v0.0.0-20250412235405-d86b230f5fbb/go.mod h1:N+NVTIIMz6icYltvaKHMvmVIllZDYUyscJ8wpcLKDZ4=
|
||||||
github.com/Picocrypt/infectious v0.0.0-20240830233326-3a050f65f9ec h1:/cop0/v0HxIJm1XGDgIlzNJ3e4HhM8nIUPZi5RZ/n1w=
|
github.com/Picocrypt/infectious v0.0.0-20250412183341-9f88c6307b39 h1:czHyPoiNrILv9xjfQ87UFllJgak8W6gVcYkmfOay/BE=
|
||||||
github.com/Picocrypt/infectious v0.0.0-20240830233326-3a050f65f9ec/go.mod h1:aaFq/WMVxrU2Exl/tXbTFSXajZrqw0mgn/wi42n0fK4=
|
github.com/Picocrypt/infectious v0.0.0-20250412183341-9f88c6307b39/go.mod h1:2ZVEanURxuWmxYZ6W6xMMy4ZR6xmQr16Vq/XPTLIspQ=
|
||||||
github.com/Picocrypt/mainthread v0.0.0-20240831004314-496f638392b3 h1:a62XmbZYhHGDR15C1gxp/IPfJX5SflrJuGpqNoOOK7w=
|
github.com/Picocrypt/mainthread v0.0.0-20240831004314-496f638392b3 h1:a62XmbZYhHGDR15C1gxp/IPfJX5SflrJuGpqNoOOK7w=
|
||||||
github.com/Picocrypt/mainthread v0.0.0-20240831004314-496f638392b3/go.mod h1:bsUKeX+/53rCTrItl3YUaeaN5tXl1v6326ZI90xIOsc=
|
github.com/Picocrypt/mainthread v0.0.0-20240831004314-496f638392b3/go.mod h1:bsUKeX+/53rCTrItl3YUaeaN5tXl1v6326ZI90xIOsc=
|
||||||
github.com/Picocrypt/serpent v0.0.0-20240830233833-9ad6ab254fd7 h1:G36G2vmQAS7CVoHQrHDGAoCWll/0kPCI8Dk7mgwcJFE=
|
github.com/Picocrypt/serpent v0.0.0-20240830233833-9ad6ab254fd7 h1:G36G2vmQAS7CVoHQrHDGAoCWll/0kPCI8Dk7mgwcJFE=
|
||||||
github.com/Picocrypt/serpent v0.0.0-20240830233833-9ad6ab254fd7/go.mod h1:BxsgRYwUVd92aEwXnXsfXfHw8aHlD/PUyExC/wwk9oI=
|
github.com/Picocrypt/serpent v0.0.0-20240830233833-9ad6ab254fd7/go.mod h1:BxsgRYwUVd92aEwXnXsfXfHw8aHlD/PUyExC/wwk9oI=
|
||||||
github.com/Picocrypt/w32 v0.0.0-20240831001500-1183079d4d57 h1:jusSXTp0h5wz8lxNXStw0jXr/ogZF6rzRF8gu0534hA=
|
github.com/Picocrypt/w32 v0.0.0-20240831001500-1183079d4d57 h1:jusSXTp0h5wz8lxNXStw0jXr/ogZF6rzRF8gu0534hA=
|
||||||
github.com/Picocrypt/w32 v0.0.0-20240831001500-1183079d4d57/go.mod h1:FkeZHdKlITdP34VknO8yLdRY5pCi+iWEhDSA0YsBhZc=
|
github.com/Picocrypt/w32 v0.0.0-20240831001500-1183079d4d57/go.mod h1:FkeZHdKlITdP34VknO8yLdRY5pCi+iWEhDSA0YsBhZc=
|
||||||
github.com/Picocrypt/zxcvbn-go v0.0.0-20240831000415-fccb38ccb913 h1:QGv9QiTkNZ2iRmXEd7nNopaUJMBhBdBcsvWPl+v51AY=
|
github.com/Picocrypt/zxcvbn-go v0.0.0-20250412183938-d59695960527 h1:IqypAzv5COsByMhiSdwlgafA5SBRG7Z0binnBSo3htM=
|
||||||
github.com/Picocrypt/zxcvbn-go v0.0.0-20240831000415-fccb38ccb913/go.mod h1:dMyJ/0E4MeBo2wH1ZYmvPTChnYSj2MjLUndvYQt0vGw=
|
github.com/Picocrypt/zxcvbn-go v0.0.0-20250412183938-d59695960527/go.mod h1:u0rcUNEwy7st1DnPxdOJdTsh0aSRhrdMOxlIGrXR1Ls=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
|
|
Loading…
Add table
Reference in a new issue