From b57e431b181a2a0e92b74b73264c419c1971f5f0 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Tue, 14 Dec 2021 21:37:17 -0500
Subject: [PATCH 01/32] Add back CodeQL
---
.github/workflows/codeql-analysis.yml | 30 +++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 .github/workflows/codeql-analysis.yml
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 0000000..76f9261
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,30 @@
+name: "CodeQL"
+on:
+ push:
+ paths:
+ - "src/Picocrypt.go"
+ pull_request:
+ branches: [ main ]
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+ strategy:
+ fail-fast: false
+ matrix:
+ language: ['go']
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v2
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ with:
+ languages: ${{ matrix.language }}
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
From 958220cf52e43650a0206d7a9c2c02ca5934fb86 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Tue, 14 Dec 2021 21:39:44 -0500
Subject: [PATCH 02/32] Test workflow
---
src/Picocrypt.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Picocrypt.go b/src/Picocrypt.go
index 80d9089..153c985 100644
--- a/src/Picocrypt.go
+++ b/src/Picocrypt.go
@@ -2,7 +2,7 @@ package main
/*
-Picocrypt v1.21
+Picocrypt v1.21
Copyright (c) Evan Su (https://evansu.cc)
Released under a GNU GPL v3 License
https://github.com/HACKERALERT/Picocrypt
From f960e09d9076587a9d37d50044fe8cef60ed3575 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Tue, 14 Dec 2021 21:47:48 -0500
Subject: [PATCH 03/32] Test again
---
src/Picocrypt.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Picocrypt.go b/src/Picocrypt.go
index 153c985..8a88ffb 100644
--- a/src/Picocrypt.go
+++ b/src/Picocrypt.go
@@ -1,7 +1,7 @@
package main
/*
-
+
Picocrypt v1.21
Copyright (c) Evan Su (https://evansu.cc)
Released under a GNU GPL v3 License
From 5250f9de57442caf817cd39632233b3379fc26da Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Tue, 14 Dec 2021 22:37:18 -0500
Subject: [PATCH 04/32] Create SECURITY.md
---
SECURITY.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 SECURITY.md
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..90f70fa
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,5 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+If you notice a security vulnerability, please contact me privately here. I don't have any bounties available, but I will put your name on the homepage as a thanks. Please do not create an Issue or post the vulnerability publicly, as it could be exploited by a hacker. Thank you.
From 37c9da3f170e845d1460ef90f59c138b21216760 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Tue, 14 Dec 2021 22:37:44 -0500
Subject: [PATCH 05/32] Update README.md
---
README.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/README.md b/README.md
index 612bf70..728c1b1 100644
--- a/README.md
+++ b/README.md
@@ -53,8 +53,6 @@ In addition to these comprehensive options for encryption and decryption, Picocr
# Security
For more information on how Picocrypt handles cryptography, see Internals 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, Google, Reddit, Ubuntu One/Snapcraft, Discord, 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. You can feel confident about using Picocrypt.
-In the unlikely scenario that a security vulnerability is discovered, please draft a security advisory in the Security tab of this repository and I will fix it as soon as possible.
-
# Community
Here are some places where you can stay up to date with Picocrypt and get involved:
From f3dcaf6c0d5b1ee3728b49990654d122da474d38 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 15 Dec 2021 16:34:59 -0500
Subject: [PATCH 06/32] Add a bug fix and two improvements
---
Changelog.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Changelog.md b/Changelog.md
index c1ac440..7944ba5 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -14,6 +14,13 @@
- Finish adding tooltips
+# v1.22 (ETA unknown for now)
+
+ - Fix keyfile order bug
+ - Remove fast mode, as a change for the normal mode will make fast mode obselete
+ - For normal mode, change HMAC-SHA3 to a keyed Blake2b
+
+
# v1.21 (Released 11/19/2021)
- ✓ Remove file shredder because it won't be very effective in the future
From 677f4736e5ab980b42c844455c67a59494e45aa7 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 15 Dec 2021 16:36:37 -0500
Subject: [PATCH 07/32] Add improving Internals.md
---
Changelog.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Changelog.md b/Changelog.md
index 7944ba5..08b4f8d 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,7 +5,7 @@
- CLI (thanks u/your_username)
-# v1.22 (A future release)
+# v1.23 (A future release)
- Associate .pcv file extension with Picocrypt
- Improve Reed-Solomon performance
@@ -16,9 +16,10 @@
# v1.22 (ETA unknown for now)
- - Fix keyfile order bug
+ - ✓ Fix keyfile order bug
- Remove fast mode, as a change for the normal mode will make fast mode obselete
- For normal mode, change HMAC-SHA3 to a keyed Blake2b
+ - Improve Internals documentation (header format, etc.)
# v1.21 (Released 11/19/2021)
From 724b96300a24a27ae3a1b0282a6e06e1cd57d53b Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 15 Dec 2021 17:15:18 -0500
Subject: [PATCH 08/32] Remove unnecessary Linux dependencies
---
src/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/README.md b/src/README.md
index 375f2b6..173878c 100644
--- a/src/README.md
+++ b/src/README.md
@@ -4,7 +4,7 @@ If you would like to run Picocrypt from source, or an executable isn't available
# 1. Prerequisites
Linux:
```bash
-apt install -y gcc make libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libgl1-mesa-dev libxxf86vm-dev libgtk-3-dev xdg-utils libglu1-mesa xclip
+apt install -y gcc libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libgl1-mesa-dev libxxf86vm-dev libgtk-3-dev libglu1-mesa xclip
```
macOS:
```bash
From c243aa657ec2d59507a6e9b0d41df98bbfe8cdd6 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 15 Dec 2021 17:17:41 -0500
Subject: [PATCH 09/32] Remove unnecessary Linux dependencies
---
src/snapcraft.yaml | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/snapcraft.yaml b/src/snapcraft.yaml
index e9d206d..8303e35 100644
--- a/src/snapcraft.yaml
+++ b/src/snapcraft.yaml
@@ -1,7 +1,7 @@
name: picocrypt
summary: A very small, very simple, yet very secure encryption tool.
description: Picocrypt is a very small, very simple, yet very secure encryption tool that you can use to protect your files, generate checksums, and much more. It's designed to be the go-to tool for encryption, with a focus on security, simplicity, and reliability. Picocrypt uses the secure XChaCha20 cipher and the SHA3 hash function to provide a high level of security, even from three-letter agencies like the NSA. It's designed for maximal security, making absolutely no compromises security-wise, and is built with Go's standard x/crypto modules. Your privacy and security is under attack. Take it back with confidence by protecting your files with Picocrypt.
-version: "1.21"
+version: "1.22"
confinement: strict
base: core20
grade: stable
@@ -20,7 +20,6 @@ parts:
source: .
build-packages:
- gcc
- - make
- libx11-dev
- libxcursor-dev
- libxrandr-dev
@@ -30,10 +29,8 @@ parts:
- libxxf86vm-dev
- libgtk-3-dev
stage-packages:
- - xclip
- - xdg-utils
- libglu1-mesa
- - language-pack-en-base
+ - xclip
after:
- desktop-glib-only
desktop-glib-only:
From c18470b11a5e32b39e6ecd621eaf6591e2493b55 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 15 Dec 2021 21:13:03 -0500
Subject: [PATCH 10/32] Update go modules
---
src/go.mod | 26 ++++++++++++++------------
src/go.sum | 28 +++++++++++++---------------
2 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/src/go.mod b/src/go.mod
index 385cc3a..0043ece 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -3,19 +3,21 @@ module Picocrypt
go 1.17
require (
- github.com/HACKERALERT/clipboard v0.1.5-0.20210716140604-61d96bf4fc94 // indirect
- github.com/HACKERALERT/dialog v0.0.0-20210716143851-223edea1d840 // indirect
- github.com/HACKERALERT/giu v0.5.7-0.20211027224501-6357f8997cac // indirect
- github.com/HACKERALERT/gl v0.0.0-20211027194356-838f5789a406 // indirect
- github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211027203414-1b9369ce24d2 // indirect
- github.com/HACKERALERT/go-findfont v0.0.0-20211027212021-2e5a409c1318 // indirect
- github.com/HACKERALERT/imgui-go v1.12.1-0.20211027214916-5870dfea82b0 // indirect
- github.com/HACKERALERT/infectious v0.0.0-20211027225427-984efcfa77a9 // indirect
- github.com/HACKERALERT/jibber_jabber v0.0.0-20210819210536-54a4d27b5376 // indirect
+ github.com/HACKERALERT/clipboard v0.1.5-0.20210716140604-61d96bf4fc94
+ github.com/HACKERALERT/dialog v0.0.0-20210716143851-223edea1d840
+ github.com/HACKERALERT/giu v0.5.7-0.20211216020632-65de69857557
+ github.com/HACKERALERT/infectious v0.0.0-20211027225427-984efcfa77a9
+ github.com/HACKERALERT/jibber_jabber v0.0.0-20210819210536-54a4d27b5376
+ github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66
+ github.com/HACKERALERT/zxcvbn-go v0.0.0-20210927200100-f131a4666ad5
+ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
+)
+
+require (
+ github.com/HACKERALERT/gl v0.0.0-20211216002416-e8bf2db61361 // indirect
+ github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211216001154-d0da149b3bef // indirect
+ github.com/HACKERALERT/imgui-go v1.12.1-0.20211216003024-285d689cbdfc // indirect
github.com/HACKERALERT/mainthread v0.0.0-20211027212305-2ec9e701cc14 // indirect
- github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66 // indirect
github.com/HACKERALERT/w32 v0.0.0-20210716142531-faa7189c4abf // indirect
- github.com/HACKERALERT/zxcvbn-go v0.0.0-20210927200100-f131a4666ad5 // indirect
- golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
)
diff --git a/src/go.sum b/src/go.sum
index 1901465..d189df0 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -2,20 +2,14 @@ github.com/HACKERALERT/clipboard v0.1.5-0.20210716140604-61d96bf4fc94 h1:FNwFtZl
github.com/HACKERALERT/clipboard v0.1.5-0.20210716140604-61d96bf4fc94/go.mod h1:kkjR9AGvIlIUJdjd/CBL1VfQvyPDE5kL31rAzY/r0s4=
github.com/HACKERALERT/dialog v0.0.0-20210716143851-223edea1d840 h1:7He527vRG3S8tVQHu+ZGl4cJ46JhjDk4/Iu1PevcE+Q=
github.com/HACKERALERT/dialog v0.0.0-20210716143851-223edea1d840/go.mod h1:oeUTfMKn6HhRJ+Lguhk21YAvURR/yk4OkeVcrHpUKbM=
-github.com/HACKERALERT/giu v0.5.7-0.20211027223400-815bf822aada h1:IwNXnaf7iL3PaDcgJXCJsDyxZHGkpI7H5QQqvbJUBz4=
-github.com/HACKERALERT/giu v0.5.7-0.20211027223400-815bf822aada/go.mod h1:zm4+x/08BnL7sAwOXqyYbaT0qT4x1FbPPeDQqKRKLhY=
-github.com/HACKERALERT/giu v0.5.7-0.20211027224501-6357f8997cac h1:8z+sBiBisd96Cf1nI+bikh4bxtmt91Zom52dlz8x8XA=
-github.com/HACKERALERT/giu v0.5.7-0.20211027224501-6357f8997cac/go.mod h1:/YCfEOyzu2UlGfOjh41QDQYe1UYCWfj09pD2nXkennk=
-github.com/HACKERALERT/gl v0.0.0-20211027194356-838f5789a406 h1:87NS0Fx0EkDPxyDmKad2951/aMJRP2mIiJ3G6jzJI+8=
-github.com/HACKERALERT/gl v0.0.0-20211027194356-838f5789a406/go.mod h1:ZUosVzfEKNGLMLk6aj9yo0FSAhWWsbTMjuzeIUXniB0=
-github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211027203414-1b9369ce24d2 h1:iuBfCfVs/+LcGafOUDXNqFr7d+5Xh5f6Yovl9gK5xG4=
-github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211027203414-1b9369ce24d2/go.mod h1:aP+FSN9tk1W3UsQisFWxRLQ4WOF7T3niq68UYw0B150=
-github.com/HACKERALERT/go-findfont v0.0.0-20211027212021-2e5a409c1318 h1:2MNs2kJfnojX01dYJFPtD//0Z03nF9mkMrsKLFvUjqc=
-github.com/HACKERALERT/go-findfont v0.0.0-20211027212021-2e5a409c1318/go.mod h1:L2Y9eyXVqwA6v+8o5lVm7vM1eokEvpZN5R1CYSp9vW4=
-github.com/HACKERALERT/imgui-go v1.12.1-0.20211027214916-5870dfea82b0 h1:DndFdVvbiyzO4sajMFQ25zwuk5pWPg7DTO5EdfdjaRw=
-github.com/HACKERALERT/imgui-go v1.12.1-0.20211027214916-5870dfea82b0/go.mod h1:778OLTsdd7wS4LLtskqROtc93vnzueQ2Y/JMYzHQHiY=
-github.com/HACKERALERT/infectious v0.0.0-20211027211519-a810ec917770 h1:oWNxph0qM9CzPww04luj8HJufveToD+VjbESPVhvWw4=
-github.com/HACKERALERT/infectious v0.0.0-20211027211519-a810ec917770/go.mod h1:gTfGH7xorTbSHZQZJ/HQBOp18E97v7zDquA2HcI9fgc=
+github.com/HACKERALERT/giu v0.5.7-0.20211216020632-65de69857557 h1:2vQfSuzTUDTQKv68HQbic5uJWa1qOBvzHJ3Rngo/70Q=
+github.com/HACKERALERT/giu v0.5.7-0.20211216020632-65de69857557/go.mod h1:rq+zzA8hlxAiO0ryOTMuPPm4d2Y4bUx5JRmIfp0fdqQ=
+github.com/HACKERALERT/gl v0.0.0-20211216002416-e8bf2db61361 h1:NMPzcOu/LpfEUf0wRZlayjlU0345ujYOWZbVKsfL6g4=
+github.com/HACKERALERT/gl v0.0.0-20211216002416-e8bf2db61361/go.mod h1:ZUosVzfEKNGLMLk6aj9yo0FSAhWWsbTMjuzeIUXniB0=
+github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211216001154-d0da149b3bef h1:MWA48bM0uKSblAiB51YtMDWEBhJtX+s3HcjlUN7o8cE=
+github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211216001154-d0da149b3bef/go.mod h1:aP+FSN9tk1W3UsQisFWxRLQ4WOF7T3niq68UYw0B150=
+github.com/HACKERALERT/imgui-go v1.12.1-0.20211216003024-285d689cbdfc h1:40aJqXAQbh6KjMN1QEzmPrwcNYCJsJH4uGS+nDa+k4Q=
+github.com/HACKERALERT/imgui-go v1.12.1-0.20211216003024-285d689cbdfc/go.mod h1:Yo2L7QsU7d+Y6+Uput8+3AHYji0EFojRg4Sokun4Xb0=
github.com/HACKERALERT/infectious v0.0.0-20211027225427-984efcfa77a9 h1:SwsesmigJhD95MYv5RTkPNvICy3bdOdKX/xbeN8Z+64=
github.com/HACKERALERT/infectious v0.0.0-20211027225427-984efcfa77a9/go.mod h1:m3JCdt8lI79U7J+QavACBRIJBxTaih+2feAHRFq82Wg=
github.com/HACKERALERT/jibber_jabber v0.0.0-20210819210536-54a4d27b5376 h1:8FNjZ8C+WOKpOlQe9vaTq3vBGC7zuFWCVvzkEE6DOHs=
@@ -30,7 +24,11 @@ github.com/HACKERALERT/zxcvbn-go v0.0.0-20210927200100-f131a4666ad5 h1:cIW3wwoeZ
github.com/HACKERALERT/zxcvbn-go v0.0.0-20210927200100-f131a4666ad5/go.mod h1:nykydiYjCDMkF/2vQXSPM38vR5N9W1DITHvupnN+eOk=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
From 0261f34d4e055e0850658314453398442a07336d Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 15 Dec 2021 21:22:24 -0500
Subject: [PATCH 11/32] Add more thanks :)
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 728c1b1..0344d4b 100644
--- a/README.md
+++ b/README.md
@@ -96,6 +96,7 @@ As well, a great thanks to these people, who have helped translate Picocrypt and
- @kurpau for Lithuanian
- u/francirc for Spanish
- yn for Russian
+ - @Etim-Orb for Hungarian
Finally, thanks to these people for helping me out when needed:
@@ -104,4 +105,5 @@ Finally, thanks to these people for helping me out when needed:
u/greenreddits for constant feedback and support
u/Tall_Escape for helping me test Picocrypt
u/NSABackdoors for doing plenty of testing
+ @samuel-lucas6 for feedback, suggestions, and support
From c3aa8edadba643384b2b63de009751e18c606b20 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Thu, 16 Dec 2021 17:09:43 -0500
Subject: [PATCH 12/32] Update go modules
---
src/go.mod | 23 ++++++++++-------------
src/go.sum | 35 +++++++++++++++--------------------
2 files changed, 25 insertions(+), 33 deletions(-)
diff --git a/src/go.mod b/src/go.mod
index 0043ece..ad1387d 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -3,21 +3,18 @@ module Picocrypt
go 1.17
require (
- github.com/HACKERALERT/clipboard v0.1.5-0.20210716140604-61d96bf4fc94
- github.com/HACKERALERT/dialog v0.0.0-20210716143851-223edea1d840
- github.com/HACKERALERT/giu v0.5.7-0.20211216020632-65de69857557
- github.com/HACKERALERT/infectious v0.0.0-20211027225427-984efcfa77a9
- github.com/HACKERALERT/jibber_jabber v0.0.0-20210819210536-54a4d27b5376
- github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66
- github.com/HACKERALERT/zxcvbn-go v0.0.0-20210927200100-f131a4666ad5
- golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
-)
-
-require (
+ github.com/HACKERALERT/clipboard v0.1.5-0.20211215214929-7345ba96aeca // indirect
+ github.com/HACKERALERT/dialog v0.0.0-20211215220206-17f428aa513e // indirect
+ github.com/HACKERALERT/giu v0.5.7-0.20211216020632-65de69857557 // indirect
github.com/HACKERALERT/gl v0.0.0-20211216002416-e8bf2db61361 // indirect
github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211216001154-d0da149b3bef // indirect
github.com/HACKERALERT/imgui-go v1.12.1-0.20211216003024-285d689cbdfc // indirect
+ github.com/HACKERALERT/infectious v0.0.0-20211215232025-9d20874ad4b1 // indirect
+ github.com/HACKERALERT/jibber_jabber v0.0.0-20211215234401-c23e432f628b // indirect
github.com/HACKERALERT/mainthread v0.0.0-20211027212305-2ec9e701cc14 // indirect
- github.com/HACKERALERT/w32 v0.0.0-20210716142531-faa7189c4abf // indirect
- golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
+ github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66 // indirect
+ github.com/HACKERALERT/w32 v0.0.0-20211215215707-4b84c2675d8d // indirect
+ github.com/HACKERALERT/zxcvbn-go v0.0.0-20210927200100-f131a4666ad5 // indirect
+ golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e // indirect
+ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
)
diff --git a/src/go.sum b/src/go.sum
index d189df0..a93318c 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -1,7 +1,7 @@
-github.com/HACKERALERT/clipboard v0.1.5-0.20210716140604-61d96bf4fc94 h1:FNwFtZlPggOVFxx1IvUtBfCGZBR7OMhsmXygJAVHkzg=
-github.com/HACKERALERT/clipboard v0.1.5-0.20210716140604-61d96bf4fc94/go.mod h1:kkjR9AGvIlIUJdjd/CBL1VfQvyPDE5kL31rAzY/r0s4=
-github.com/HACKERALERT/dialog v0.0.0-20210716143851-223edea1d840 h1:7He527vRG3S8tVQHu+ZGl4cJ46JhjDk4/Iu1PevcE+Q=
-github.com/HACKERALERT/dialog v0.0.0-20210716143851-223edea1d840/go.mod h1:oeUTfMKn6HhRJ+Lguhk21YAvURR/yk4OkeVcrHpUKbM=
+github.com/HACKERALERT/clipboard v0.1.5-0.20211215214929-7345ba96aeca h1:yZj12M2feFkat7l0r7s7QnKjJ4Q6UZOwVHroR4kQ4Nk=
+github.com/HACKERALERT/clipboard v0.1.5-0.20211215214929-7345ba96aeca/go.mod h1:kkjR9AGvIlIUJdjd/CBL1VfQvyPDE5kL31rAzY/r0s4=
+github.com/HACKERALERT/dialog v0.0.0-20211215220206-17f428aa513e h1:3tBZg/OOLNsQrSf5gZc8y3fXiHVxl+kg/iTs0maa0BA=
+github.com/HACKERALERT/dialog v0.0.0-20211215220206-17f428aa513e/go.mod h1:GxPIEf2nKp6Gx+sdpjwTdFIGmW5kj6Jta7rRO50TgpU=
github.com/HACKERALERT/giu v0.5.7-0.20211216020632-65de69857557 h1:2vQfSuzTUDTQKv68HQbic5uJWa1qOBvzHJ3Rngo/70Q=
github.com/HACKERALERT/giu v0.5.7-0.20211216020632-65de69857557/go.mod h1:rq+zzA8hlxAiO0ryOTMuPPm4d2Y4bUx5JRmIfp0fdqQ=
github.com/HACKERALERT/gl v0.0.0-20211216002416-e8bf2db61361 h1:NMPzcOu/LpfEUf0wRZlayjlU0345ujYOWZbVKsfL6g4=
@@ -10,25 +10,20 @@ github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211216001154-d0da149b3bef h1:MWA4
github.com/HACKERALERT/glfw/v3.3/glfw v0.0.0-20211216001154-d0da149b3bef/go.mod h1:aP+FSN9tk1W3UsQisFWxRLQ4WOF7T3niq68UYw0B150=
github.com/HACKERALERT/imgui-go v1.12.1-0.20211216003024-285d689cbdfc h1:40aJqXAQbh6KjMN1QEzmPrwcNYCJsJH4uGS+nDa+k4Q=
github.com/HACKERALERT/imgui-go v1.12.1-0.20211216003024-285d689cbdfc/go.mod h1:Yo2L7QsU7d+Y6+Uput8+3AHYji0EFojRg4Sokun4Xb0=
-github.com/HACKERALERT/infectious v0.0.0-20211027225427-984efcfa77a9 h1:SwsesmigJhD95MYv5RTkPNvICy3bdOdKX/xbeN8Z+64=
-github.com/HACKERALERT/infectious v0.0.0-20211027225427-984efcfa77a9/go.mod h1:m3JCdt8lI79U7J+QavACBRIJBxTaih+2feAHRFq82Wg=
-github.com/HACKERALERT/jibber_jabber v0.0.0-20210819210536-54a4d27b5376 h1:8FNjZ8C+WOKpOlQe9vaTq3vBGC7zuFWCVvzkEE6DOHs=
-github.com/HACKERALERT/jibber_jabber v0.0.0-20210819210536-54a4d27b5376/go.mod h1:LhYTEuww35ooZaTD3BIjp9CyZqwv++gAnanWXKdiWJ8=
+github.com/HACKERALERT/infectious v0.0.0-20211215232025-9d20874ad4b1 h1:+Qp4t5OHvokaLdZO7Y1i7pX+i85kNtrHRzCg7S+Lj/s=
+github.com/HACKERALERT/infectious v0.0.0-20211215232025-9d20874ad4b1/go.mod h1:Nr/zKoJunuzBaGVWwWRNz9F2D3iEXuBNWjRu3tgunYI=
+github.com/HACKERALERT/jibber_jabber v0.0.0-20211215234401-c23e432f628b h1:/oowRlgjIPB9nTJGoRssumobxLz0q0GL5Sump5Wf3Vo=
+github.com/HACKERALERT/jibber_jabber v0.0.0-20211215234401-c23e432f628b/go.mod h1:LhYTEuww35ooZaTD3BIjp9CyZqwv++gAnanWXKdiWJ8=
github.com/HACKERALERT/mainthread v0.0.0-20211027212305-2ec9e701cc14 h1:DwWXverhu/dEsPM/GPykuHGh4SxW69DaGZL5t3fANG4=
github.com/HACKERALERT/mainthread v0.0.0-20211027212305-2ec9e701cc14/go.mod h1:jW534e7roGur9mmzAfPxZLQzKXZ+GE5+XeS7PSyqPbo=
github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66 h1:YDpFq+y6mRcu97rn/rhYg8u8FdeO0wzTuLgM2gVkA+c=
github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66/go.mod h1:d/+9q3sIxtIyOgHNgFGr3yGBKKVn5h3vL4hV1qlmoLs=
-github.com/HACKERALERT/w32 v0.0.0-20210716142531-faa7189c4abf h1:L25UAtgOR6GC0TrLxaIx1Q3vUhd3SilulIPuEw+PkxI=
-github.com/HACKERALERT/w32 v0.0.0-20210716142531-faa7189c4abf/go.mod h1:S+3Ad2AEm5MhhuHJeAaXUmyAXON0qFDxcP/Chw8q7+Y=
+github.com/HACKERALERT/w32 v0.0.0-20211215215707-4b84c2675d8d h1:Ey0tgsr4MbX64pkl7Di26yUUQGG3mmZw8gUWqhQeW5g=
+github.com/HACKERALERT/w32 v0.0.0-20211215215707-4b84c2675d8d/go.mod h1:S+3Ad2AEm5MhhuHJeAaXUmyAXON0qFDxcP/Chw8q7+Y=
github.com/HACKERALERT/zxcvbn-go v0.0.0-20210927200100-f131a4666ad5 h1:cIW3wwoeZ6zru8VhdoGlZAinG+6ObzHx7BgQxUfhF34=
github.com/HACKERALERT/zxcvbn-go v0.0.0-20210927200100-f131a4666ad5/go.mod h1:nykydiYjCDMkF/2vQXSPM38vR5N9W1DITHvupnN+eOk=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
-golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU=
+golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
+golang.org/x/sys v0.0.0-20211215211219-4abf325e0275/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
From b849b4e00d1c0f484c07b9e02f0aaf8aa7a8cde3 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Thu, 16 Dec 2021 17:16:20 -0500
Subject: [PATCH 13/32] Also scan when updating go.mod and go.sum
---
.github/workflows/codeql-analysis.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 76f9261..8f0987f 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -3,6 +3,8 @@ on:
push:
paths:
- "src/Picocrypt.go"
+ - "src/go.mod"
+ - "src/go.sum"
pull_request:
branches: [ main ]
jobs:
From 1787376951a41b78fbb68edca561d56bdadbb63f Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Thu, 16 Dec 2021 17:19:28 -0500
Subject: [PATCH 14/32] Successfully removed fast mode and updated normal
---
Changelog.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Changelog.md b/Changelog.md
index 08b4f8d..0b6c500 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -16,9 +16,9 @@
# v1.22 (ETA unknown for now)
- - ✓ Fix keyfile order bug
- - Remove fast mode, as a change for the normal mode will make fast mode obselete
- - For normal mode, change HMAC-SHA3 to a keyed Blake2b
+ - ✓
Fix keyfile order bug (redundant, so there was no bug)
+ - ✓ Remove fast mode, as a change for the normal mode will make fast mode obselete
+ - ✓ For normal mode, change HMAC-SHA3 to a keyed Blake2b
- Improve Internals documentation (header format, etc.)
From a704fc4efd747dfde485a302ed2cbc196cb7b88e Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Thu, 16 Dec 2021 17:20:01 -0500
Subject: [PATCH 15/32] Remove unfeasible new features
---
Changelog.md | 2 --
1 file changed, 2 deletions(-)
diff --git a/Changelog.md b/Changelog.md
index 0b6c500..6bf271d 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,7 +1,5 @@
# Features Under Consideration
- - Get Picocrypt audited (please donate to help fund the audit)
- - Hidden volume like VeraCrypt (thanks u/greenreddits)
- CLI (thanks u/your_username)
From 9432aca8b32804aa7b96e0b90697982a04e7bcda Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Thu, 16 Dec 2021 20:57:44 -0500
Subject: [PATCH 16/32] Remove fast mode and update accordingly
---
README.md | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 0344d4b..e693947 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
English | Français | Español | Deutsch | Português | Türkçe | 中文 | русский
-Picocrypt is a very small (hence Pico), very simple, yet very secure encryption tool that you can use to protect your files, generate checksums, and much more. It's designed to be the go-to tool for encryption, with a focus on security, simplicity, and reliability. Picocrypt uses the secure XChaCha20 cipher and the SHA3 hash function to provide a high level of security, even from three-letter agencies like the NSA. It's designed for maximal security, making absolutely no compromises security-wise, and is built with Go's standard x/crypto modules. Your privacy and security is under attack. Take it back with confidence by protecting your files with Picocrypt.
+Picocrypt is a very small (hence Pico), very simple, yet very secure encryption tool that you can use to protect your files, generate checksums, and much more. It's designed to be the go-to tool for encryption, with a focus on security, simplicity, and reliability. Picocrypt uses the secure XChaCha20 cipher and the Argon2 key derivation algorithm to provide a high level of security, even from three-letter agencies like the NSA. It's designed for maximal security, making absolutely no compromises security-wise, and is built with Go's standard x/crypto modules. Your privacy and security is under attack. Take it back with confidence by protecting your files with Picocrypt.
@@ -27,7 +27,7 @@ Why should you use Picocrypt instead of BitLocker, NordLocker, VeraCrypt, AxCryp
Picocrypt is tiny. While NordLocker is over 100MB and VeraCrypt is over 30MB, Picocrypt sits at just 3MB, about the size of a high-resolution photo. And that's not all - Picocrypt is portable (doesn't need to be installed) and doesn't require administrator/root privileges.
Picocrypt is easier and more productive to use than VeraCrypt. To encrypt files with VeraCrypt, you'd have to spend at least five minutes setting up a volume. With Picocrypt's simple UI, all you have to do is drag and drop your files, enter a password, and hit Start. All the complex procedures are handled by Picocrypt internally. Who said secure encryption can't be simple?
Picocrypt is designed for security. 7-Zip is an archive utility and not an encryption tool, so its focus is not on security. Picocrypt, however, is built with security as the number one priority. Every part of Picocrypt exists for a reason and anything that could impact the security of Picocrypt is removed. Picocrypt is built with cryptography you can trust.
- Picocrypt authenticates data in addition to protecting it, preventing hackers from maliciously modifying sensitive data. This is useful when you are sending encrypted files over an insecure channel and want to be sure that it arrives untouched. Picocrypt uses HMAC-SHA3 for authenticity, which is a highly secure hash function in a well-known construction.
+ Picocrypt authenticates data in addition to protecting it, preventing hackers from maliciously modifying sensitive data. This is useful when you are sending encrypted files over an insecure channel and want to be sure that it arrives untouched.
Picocrypt actively protects encrypted header data from corruption by adding extra Reed-Solomon parity bytes, so if part of a volume's header (which contains important cryptographic components) corrupts (e.g., hard drive bit rot), Picocrypt can still recover the header and decrypt your data with a high success rate. Picocrypt can also encode the entire volume with Reed-Solomon to prevent any corruption to your important files.
@@ -41,8 +41,7 @@ While being simple, Picocrypt also strives to be powerful in the hands of knowle
Password generator: Picocrypt provides a secure password generator that you can use to create cryptographically secure passwords. You can customize the password length, as well as the types of characters to include.
File metadata: Use this to store notes, information, and text along with the file (it won't be encrypted). For example, you can put a description of the file you're encrypting before sending it to someone. When the person you sent it to drops the file into Picocrypt, your description will be shown to that person.
Keyfiles: Picocrypt supports the use of keyfiles as an additional form of authentication. Not only can you use multiple keyfiles, but you can also require the correct order of keyfiles to be present, for a successful decryption to occur. A particularly good use case of multiple keyfiles is creating a shared volume, where each person holds a keyfile, and all of them (and their keyfiles) must be present in order to decrypt the shared volume.
- Fast mode: Using this mode will greatly speed up encryption/decryption. In this mode, BLAKE2b will be used to authenticate data instead of SHA3, and Argon2 parameters will be lowered. Doing this provides higher speeds, but at a lower security margin. If all you need to do is encrypt some low-sensitivity files, this option can be a useful and performant choice.
- Paranoid mode: Using this mode will encrypt your data with both XChaCha20 and Serpent in a cascade fashion. This is recommended for protecting top-secret files and provides the highest level of practical security attainable. In order for a hacker to crack your encrypted data, both the XChaCha20 cipher and the Serpent cipher must be broken, assuming you've chosen a good password.
+ Paranoid mode: Using this mode will encrypt your data with both XChaCha20 and Serpent in a cascade fashion, and use HMAC-SHA3 to authenticate data instead of BLAKE2b. This is recommended for protecting top-secret files and provides the highest level of practical security attainable. In order for a hacker to crack your encrypted data, both the XChaCha20 cipher and the Serpent cipher must be broken, assuming you've chosen a good password.
Prevent corruption using Reed-Solomon: This feature is very useful if you are planning to archive important data on a cloud provider or external medium for a long time. If checked, Picocrypt will use the Reed-Solomon error correction code to add 8 extra bytes for every 128 bytes to prevent file corruption. This means that up to ~3% of your file can corrupt and Picocrypt will still be able to correct the errors and decrypt your files with no corruption. Of course, if your file corrupts very badly (e.g., you dropped your hard drive), Picocrypt won't be able to fully recover your files, but it will try its best to recover what it can. Note that this option will slow down encryption and decryption considerably.
Keep decrypted output even if it's corrupted or modified: Picocrypt automatically checks for integrity upon decryption. If the file has been modified or is corrupted, Picocrypt will automatically delete the output for the user's safety. If you want to keep the corrupted or modified data after decryption, check this option. Also, if this option is checked and the Reed-Solomon feature was used on the encrypted file, Picocrypt will attempt to recover as much of the file as possible during decryption.
Split files into chunks: Don't feel like dealing with gargantuan files? No worries! With Picocrypt, you can choose to split your output file into custom-sized chunks, so large files can become more manageable and easier to upload to cloud providers. Simply choose a unit (KiB, MiB, or GiB) and enter your desired number for that unit. To decrypt the chunks, simply drag one of them into Picocrypt, and the chunks will be automatically recombined during decryption.
From 213ebd730a50bc0a74a51a3580b3f39345e7a05f Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Thu, 16 Dec 2021 21:06:46 -0500
Subject: [PATCH 17/32] Update description
---
src/snapcraft.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/snapcraft.yaml b/src/snapcraft.yaml
index 8303e35..7ac0aea 100644
--- a/src/snapcraft.yaml
+++ b/src/snapcraft.yaml
@@ -1,6 +1,6 @@
name: picocrypt
summary: A very small, very simple, yet very secure encryption tool.
-description: Picocrypt is a very small, very simple, yet very secure encryption tool that you can use to protect your files, generate checksums, and much more. It's designed to be the go-to tool for encryption, with a focus on security, simplicity, and reliability. Picocrypt uses the secure XChaCha20 cipher and the SHA3 hash function to provide a high level of security, even from three-letter agencies like the NSA. It's designed for maximal security, making absolutely no compromises security-wise, and is built with Go's standard x/crypto modules. Your privacy and security is under attack. Take it back with confidence by protecting your files with Picocrypt.
+description: Picocrypt is a very small, very simple, yet very secure encryption tool that you can use to protect your files, generate checksums, and much more. It's designed to be the go-to tool for encryption, with a focus on security, simplicity, and reliability. Picocrypt uses the secure XChaCha20 cipher and the Argon2 key derivation function to provide a high level of security, even from three-letter agencies like the NSA. It's designed for maximal security, making absolutely no compromises security-wise, and is built with Go's standard x/crypto modules. Your privacy and security is under attack. Take it back with confidence by protecting your files with Picocrypt.
version: "1.22"
confinement: strict
base: core20
From 9167817d22fa627b5977d86a9200bf2f6b859783 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Thu, 16 Dec 2021 21:23:24 -0500
Subject: [PATCH 18/32] Improve wording
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e693947..71131e3 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
English | Français | Español | Deutsch | Português | Türkçe | 中文 | русский
-Picocrypt is a very small (hence Pico), very simple, yet very secure encryption tool that you can use to protect your files, generate checksums, and much more. It's designed to be the go-to tool for encryption, with a focus on security, simplicity, and reliability. Picocrypt uses the secure XChaCha20 cipher and the Argon2 key derivation algorithm to provide a high level of security, even from three-letter agencies like the NSA. It's designed for maximal security, making absolutely no compromises security-wise, and is built with Go's standard x/crypto modules. Your privacy and security is under attack. Take it back with confidence by protecting your files with Picocrypt.
+Picocrypt is a very small (hence Pico), very simple, yet very secure encryption tool that you can use to protect your files, generate checksums, and much more. It's designed to be the go-to tool for encryption, with a focus on security, simplicity, and reliability. Picocrypt uses the secure XChaCha20 cipher and the Argon2 key derivation function to provide a high level of security, even from three-letter agencies like the NSA. It's designed for maximal security, making absolutely no compromises security-wise, and is built with Go's standard x/crypto modules. Your privacy and security is under attack. Take it back with confidence by protecting your files with Picocrypt.
From f80a9f9236a465dd8eba60f9ac9a6326b45f832e Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Thu, 16 Dec 2021 21:36:53 -0500
Subject: [PATCH 19/32] Update to latest parameters and functions
---
Internals.md | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/Internals.md b/Internals.md
index b1e75b0..00807b9 100644
--- a/Internals.md
+++ b/Internals.md
@@ -4,10 +4,9 @@ If you're wondering about how Picocrypt handles cryptography, you've come to the
# Core Cryptography
Picocrypt uses the following cryptographic primitives:
- XChaCha20 (cascaded with Serpent in CTR mode for paranoid mode)
-- HMAC-SHA3 for normal and paranoid mode, keyed-BLAKE2b for fast mode (256-bit key, 512-bit digest)
-- HKDF-SHA3-256 for deriving a subkey used with the MAC above, as well as a key for Serpent
+- Keyed-BLAKE2b for normal mode, HMAC-SHA3 for paranoid mode (256-bit key, 512-bit digest)
+- HKDF-SHA3 for deriving a subkey used with the MAC above, as well as a key for Serpent
- Argon2id:
- - Fast mode: 4 passes, 128 MiB memory, 4 threads
- Normal mode: 4 passes, 1 GiB memory, 4 threads
- Paranoid mode: 8 passes, 1 GiB memory, 8 threads
@@ -19,3 +18,6 @@ Picocrypt allows the use of keyfiles as an additional (or only) form of authenti
If "Require correct order" is not checked, Picocrypt will take the SHA3 hash of each file individually, and XORs the hashes together. Finally, the result is XORed to the master key. Because the XOR operation is both commutative and associative, the order in which the keyfiles hashes are XORed to each other doesn't matter -- the end result is the same.
If "Require correct order" is checked, Picocrypt will combine (concatenate) the files together in the order they were dropped into the window, and take the SHA3 hash of the combined keyfiles. If the order is not correct, the keyfiles, when appended to each other, will result in a different file, and therefore a different hash. Thus, the correct order of keyfiles is required to successfully decrypt the volume.
+
+# Header Format
+Work in progress...
From bb9cac7f578a95ec5cc96b470e814c65e4a42ad0 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Mon, 20 Dec 2021 12:54:41 -0500
Subject: [PATCH 20/32] Testing CodeQL
---
.github/workflows/codeql-analysis.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 8f0987f..6afcd21 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,6 +1,7 @@
name: "CodeQL"
on:
push:
+ branches: [ main ]
paths:
- "src/Picocrypt.go"
- "src/go.mod"
From f3a5634b0ad2cc47888febc8b485f4b3a59b5e2f Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 11:30:11 -0500
Subject: [PATCH 21/32] Add ETA, move things around
---
Changelog.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Changelog.md b/Changelog.md
index 6bf271d..6416465 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -10,14 +10,14 @@
Remove EXIF data from keyfiles
Customizable Argon2 parameters
Finish adding tooltips
+ Improve Internals documentation (header format, etc.)
-# v1.22 (ETA unknown for now)
+# v1.22 (ETA 1 week)
- - ✓
Fix keyfile order bug (redundant, so there was no bug)
+ - ✓
Fix keyfile order bug (redundant, so there actually was no bug)
- ✓ Remove fast mode, as a change for the normal mode will make fast mode obselete
- ✓ For normal mode, change HMAC-SHA3 to a keyed Blake2b
- - Improve Internals documentation (header format, etc.)
# v1.21 (Released 11/19/2021)
From bdca4f57c472aecb68cd52d164deb6015b26c08d Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 11:30:41 -0500
Subject: [PATCH 22/32] Picocrypt is stable, so deleting unstable folder
---
src/unstable/Picocrypt.go | 2214 -------------------------------------
src/unstable/README.md | 1 -
2 files changed, 2215 deletions(-)
delete mode 100644 src/unstable/Picocrypt.go
delete mode 100644 src/unstable/README.md
diff --git a/src/unstable/Picocrypt.go b/src/unstable/Picocrypt.go
deleted file mode 100644
index 153c985..0000000
--- a/src/unstable/Picocrypt.go
+++ /dev/null
@@ -1,2214 +0,0 @@
-package main
-
-/*
-
-Picocrypt v1.21
-Copyright (c) Evan Su (https://evansu.cc)
-Released under a GNU GPL v3 License
-https://github.com/HACKERALERT/Picocrypt
-
-~ In cryptography we trust ~
-
-*/
-
-import (
- _ "embed"
-
- // Generic
- "archive/zip"
- "bytes"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "hash"
- "image"
- "image/color"
- "image/png"
- "io"
- "math"
- "math/big"
- "net/http"
- "os"
- "path/filepath"
- "regexp"
- "runtime/debug"
- "strconv"
- "strings"
- "time"
-
- // Cryptography
- "crypto/cipher"
- "crypto/hmac"
- "crypto/md5"
- "crypto/rand"
- "crypto/sha1"
- "crypto/sha256"
- "crypto/subtle"
-
- "github.com/HACKERALERT/serpent"
- "golang.org/x/crypto/argon2"
- "golang.org/x/crypto/blake2b"
- "golang.org/x/crypto/blake2s"
- "golang.org/x/crypto/chacha20"
- "golang.org/x/crypto/hkdf"
- "golang.org/x/crypto/sha3"
-
- // UI
- "github.com/HACKERALERT/giu"
-
- // Reed-Solomon
- "github.com/HACKERALERT/infectious"
-
- // Helpers
- "github.com/HACKERALERT/clipboard"
- "github.com/HACKERALERT/dialog"
- "github.com/HACKERALERT/jibber_jabber"
- "github.com/HACKERALERT/zxcvbn-go"
-)
-
-//go:embed icon.png
-var icon []byte
-
-//go:embed font.ttf
-var font []byte
-
-//go:embed strings.json
-var localeBytes []byte
-
-// Localization
-type locale struct {
- iso string
- data []string
-}
-
-var locales []locale
-var selectedLocale = "en"
-var allLocales = []string{
- "en",
-}
-var languages = []string{
- "English",
-}
-var languageSelected int32
-
-// Generic variables
-var version = "v1.21"
-var window *giu.MasterWindow
-var windowOptimized bool
-var dpi float32
-var tab = 0
-var mode string
-var working bool
-var recombine bool
-
-// Three variables store the input files
-var onlyFiles []string
-var onlyFolders []string
-var allFiles []string
-
-// Input file variables
-var inputLabel = "Drop files and folders into this window."
-var inputFile string
-
-// Password variables
-var password string
-var cPassword string
-var passwordStrength int
-var passwordState = giu.InputTextFlagsPassword
-var passwordStateLabel = "Show"
-
-// Password generator variables
-var showGenpass = false
-var genpassCopy = true
-var genpassLength int32 = 32
-var genpassUpper = true
-var genpassLower = true
-var genpassNums = true
-var genpassSymbols = true
-
-// Keyfile variables
-var keyfile bool
-var keyfiles []string
-var keyfileOrderMatters bool
-var keyfilePrompt = "None selected."
-var showKeyfile bool
-
-// Metadata variables
-var metadata string
-var metadataPrompt = "Metadata:"
-var metadataDisabled bool
-
-// Advanced options
-var fast bool
-var paranoid bool
-var reedsolo bool
-var deleteWhenDone bool
-var split bool
-var splitSize string
-var splitUnits = []string{
- "KiB",
- "MiB",
- "GiB",
-}
-var splitSelected int32 = 1
-var compress bool
-var keep bool
-var kept bool
-
-// Output file variables
-var outputFile string
-
-// Status variables
-var mainStatus = "Ready."
-var mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
-var popupStatus string
-
-// Progress variables
-var progress float32
-var progressInfo string
-var showProgress bool
-
-// Confirm overwrite variables
-var showConfirmation bool
-
-// Reed-Solomon encoders
-var rs1, _ = infectious.NewFEC(1, 3) // 1 data shard, 3 total -> 2 parity shards
-var rs5, _ = infectious.NewFEC(5, 15)
-var rs6, _ = infectious.NewFEC(6, 18)
-var rs16, _ = infectious.NewFEC(16, 48)
-var rs24, _ = infectious.NewFEC(24, 72)
-var rs32, _ = infectious.NewFEC(32, 96)
-var rs64, _ = infectious.NewFEC(64, 192)
-var rs128, _ = infectious.NewFEC(128, 136)
-
-// File checksum generator variables
-var csMd5 string
-var csSha1 string
-var csSha256 string
-var csSha3 string
-var csBlake2b string
-var csBlake2s string
-var csValidate string
-var md5Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
-var sha1Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
-var sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
-var sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
-var blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
-var blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
-var csProgress float32 = 0
-var md5Selected = true
-var sha1Selected = true
-var sha256Selected = true
-var sha3Selected = false
-var blake2bSelected = false
-var blake2sSelected = false
-
-func draw() {
- giu.SingleWindow().Flags(giu.WindowFlagsNoDecoration|giu.WindowFlagsNoNavFocus|giu.WindowFlagsNoMove|
- giu.WindowFlagsNoScrollWithMouse).Layout(
- giu.Custom(func() {
- pos := giu.GetCursorPos()
- w, _ := giu.CalcTextSize(languages[languageSelected])
- giu.Row(
- giu.Dummy(-w/dpi-34, 0),
- giu.Combo("##language", languages[languageSelected], languages, &languageSelected).OnChange(func() {
- selectedLocale = allLocales[languageSelected]
- }).Size(w/dpi+26),
- ).Build()
- giu.SetCursorPos(pos)
-
- giu.TabBar().TabItems(
- giu.TabItem(s("Encryption")).Layout(
- giu.Custom(func() {
- if giu.IsItemActive() {
- tab = 0
- }
- }),
-
- giu.Custom(func() {
- if showGenpass {
- giu.PopupModal(s("Generate password:")).
- Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize).Layout(
- giu.Row(
- giu.Label(s("Length:")),
- giu.SliderInt(&genpassLength, 4, 64).Size(giu.Auto),
- ),
- giu.Checkbox(s("Uppercase"), &genpassUpper),
- giu.Checkbox(s("Lowercase"), &genpassLower),
- giu.Checkbox(s("Numbers"), &genpassNums),
- giu.Checkbox(s("Symbols"), &genpassSymbols),
- giu.Checkbox(s("Copy to clipboard"), &genpassCopy),
- giu.Row(
- giu.Button(s("Cancel")).Size(100, 0).OnClick(func() {
- giu.CloseCurrentPopup()
- showGenpass = false
- }),
- giu.Button(s("Generate")).Size(100, 0).OnClick(func() {
- tmp := genPassword()
- password = tmp
- cPassword = tmp
- passwordStrength = zxcvbn.PasswordStrength(password, nil).Score
- giu.CloseCurrentPopup()
- showGenpass = false
- giu.Update()
- }),
- ),
- ).Build()
- giu.OpenPopup(s("Generate password:"))
- giu.Update()
- }
- }),
-
- giu.Custom(func() {
- if showKeyfile {
- giu.PopupModal(s("Manage keyfiles:")).
- Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize|giu.WindowFlagsAlwaysAutoResize).Layout(
- giu.Label(s("Drag and drop your keyfiles here.")),
- giu.Custom(func() {
- if mode != "decrypt" {
- giu.Checkbox(s("Require correct keyfile order"), &keyfileOrderMatters).Build()
- giu.Tooltip(s("If checked, you will need to drop keyfiles in the correct order.")).Build()
- } else if keyfileOrderMatters {
- giu.Label(s("The correct order of keyfiles is required.")).Build()
- }
- }),
-
- giu.Custom(func() {
- for _, i := range keyfiles {
- giu.Row(
- giu.SmallButton("×").OnClick(func() {
- var tmp []string
- for _, j := range keyfiles {
- if j != i {
- tmp = append(tmp, j)
- }
- }
- keyfiles = tmp
- if len(keyfiles) == 0 {
- keyfilePrompt = s("None selected.")
- } else if len(keyfiles) == 1 {
- keyfilePrompt = s("Using 1 keyfile.")
- } else {
- keyfilePrompt = fmt.Sprintf(s("Using %d keyfiles."), len(keyfiles))
- }
- }),
- giu.Label(filepath.Base(i)),
- ).Build()
-
- }
- }),
- giu.Row(
- giu.Button(s("Clear")).Size(150, 0).OnClick(func() {
- keyfiles = nil
- keyfilePrompt = s("None selected.")
- }),
- giu.Tooltip(s("Remove all keyfiles.")),
- giu.Button(s("Done")).Size(150, 0).OnClick(func() {
- giu.CloseCurrentPopup()
- showKeyfile = false
- }),
- ),
- ).Build()
- giu.OpenPopup(s("Manage keyfiles:"))
- giu.Update()
- }
- }),
-
- giu.Custom(func() {
- if showConfirmation {
- giu.PopupModal(s("Warning:")).
- Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize).Layout(
- giu.Label(s("Output already exists. Overwrite?")),
- giu.Row(
- giu.Button(s("No")).Size(100, 0).OnClick(func() {
- giu.CloseCurrentPopup()
- showConfirmation = false
- }),
- giu.Button(s("Yes")).Size(100, 0).OnClick(func() {
- giu.CloseCurrentPopup()
- showConfirmation = false
- showProgress = true
- giu.Update()
- go func() {
- work()
- working = false
- showProgress = false
- debug.FreeOSMemory()
- giu.Update()
- }()
- }),
- ),
- ).Build()
- giu.OpenPopup(s("Warning:"))
- giu.Update()
- }
- }),
-
- giu.Custom(func() {
- if showProgress {
- giu.PopupModal(" ").
- Flags(giu.WindowFlagsNoMove|giu.WindowFlagsNoResize).Layout(
- giu.Custom(func() {
- if !working {
- giu.CloseCurrentPopup()
- }
- }),
- giu.Row(
- giu.ProgressBar(progress).Size(280, 0).Overlay(progressInfo),
- giu.Button(s("Cancel")).Size(58, 0).OnClick(func() {
- working = false
- }),
- ),
- giu.Label(popupStatus),
- ).Build()
- giu.OpenPopup(" ")
- giu.Update()
- }
- }),
-
- giu.Row(
- giu.Label(inputLabel),
- giu.Custom(func() {
- bw, _ := giu.CalcTextSize(s("Clear"))
- p, _ := giu.GetWindowPadding()
- bw += p * 2
- giu.Dummy(float32(float64(-(bw+p)/dpi)), 0).Build()
- giu.SameLine()
- giu.Style().SetDisabled(len(allFiles) == 0 && len(onlyFiles) == 0).To(
- giu.Button(s("Clear")).Size(bw/dpi, 0).OnClick(resetUI),
- giu.Tooltip(s("Clear all input files and reset UI state.")),
- ).Build()
- }),
- ),
-
- giu.Separator(),
-
- giu.Style().SetDisabled(len(allFiles) == 0 && len(onlyFiles) == 0).To(
- giu.Row(
- giu.Label(s("Password:")),
- giu.Dummy(-124, 0),
- giu.Style().SetDisabled(mode == "decrypt" && !keyfile).To(
- giu.Label(s("Keyfiles:")),
- ),
- ),
- giu.Row(
- giu.Button(s(passwordStateLabel)).Size(54, 0).OnClick(func() {
- if passwordState == giu.InputTextFlagsPassword {
- passwordState = giu.InputTextFlagsNone
- passwordStateLabel = "Hide"
- } else {
- passwordState = giu.InputTextFlagsPassword
- passwordStateLabel = "Show"
- }
- }),
-
- giu.Button(s("Clear")).Size(54, 0).OnClick(func() {
- password = ""
- cPassword = ""
- }),
-
- giu.Button(s("Copy")).Size(54, 0).OnClick(func() {
- clipboard.WriteAll(password)
- }),
-
- giu.Button(s("Paste")).Size(54, 0).OnClick(func() {
- tmp, _ := clipboard.ReadAll()
- password = tmp
- if mode != "decrypt" {
- cPassword = tmp
- }
- passwordStrength = zxcvbn.PasswordStrength(password, nil).Score
- giu.Update()
- }),
-
- giu.Style().SetDisabled(mode == "decrypt").To(
- giu.Button(s("Create")).Size(54, 0).OnClick(func() {
- showGenpass = true
- }),
- ),
-
- giu.Style().SetDisabled(mode == "decrypt" && !keyfile).To(
- giu.Row(
- giu.Button(s("Edit")).Size(54, 0).OnClick(func() {
- showKeyfile = true
- }),
- giu.Style().SetDisabled(mode == "decrypt").To(
- giu.Button(s("Create")).Size(54, 0).OnClick(func() {
- file, _ := dialog.File().Title(s("Save keyfile as:")).Save()
- if file == "" {
- return
- }
- fout, _ := os.Create(file)
- data := make([]byte, 1048576)
- rand.Read(data)
- fout.Write(data)
- fout.Close()
- }),
- ),
- ),
- ),
- ),
- giu.Row(
- giu.InputText(&password).Flags(passwordState).Size(302/dpi).OnChange(func() {
- passwordStrength = zxcvbn.PasswordStrength(password, nil).Score
- }),
- giu.Custom(func() {
- c := giu.GetCanvas()
- p := giu.GetCursorScreenPos()
-
- var col color.RGBA
- switch passwordStrength {
- case 0:
- col = color.RGBA{0xc8, 0x4c, 0x4b, 0xff}
- case 1:
- col = color.RGBA{0xa9, 0x6b, 0x4b, 0xff}
- case 2:
- col = color.RGBA{0x8a, 0x8a, 0x4b, 0xff}
- case 3:
- col = color.RGBA{0x6b, 0xa9, 0x4b, 0xff}
- case 4:
- col = color.RGBA{0x4c, 0xc8, 0x4b, 0xff}
- }
- if password == "" || mode == "decrypt" {
- col = color.RGBA{0xff, 0xff, 0xff, 0x00}
- }
-
- path := p.Add(image.Pt(
- int(math.Round(float64(-20*dpi))),
- int(math.Round(float64(12*dpi))),
- ))
- c.PathArcTo(path, 6*dpi, -math.Pi/2, float32(passwordStrength+1)/5*2*math.Pi-math.Pi/2, -1)
- c.PathStroke(col, false, 2)
- }),
- giu.Style().SetDisabled(true).To(
- giu.InputText(&keyfilePrompt).Size(giu.Auto),
- ),
- ),
- ),
-
- giu.Style().SetDisabled(password == "").To(
- giu.Row(
- giu.Style().SetDisabled(mode == "decrypt").To(
- giu.Label(s("Confirm password:")),
- ),
- giu.Dummy(-124, 0),
- giu.Style().SetDisabled(true).To(
- giu.Label(s("Custom Argon2:")),
- ),
- ),
- ),
- giu.Style().SetDisabled(password == "").To(
- giu.Row(
- giu.Style().SetDisabled(mode == "decrypt").To(
- giu.Row(
- giu.InputText(&cPassword).Flags(passwordState).Size(302/dpi),
- giu.Custom(func() {
- c := giu.GetCanvas()
- p := giu.GetCursorScreenPos()
- col := color.RGBA{0x4c, 0xc8, 0x4b, 0xff}
-
- if cPassword != password {
- col = color.RGBA{0xc8, 0x4c, 0x4b, 0xff}
- }
- if password == "" || cPassword == "" || mode == "decrypt" {
- col = color.RGBA{0xff, 0xff, 0xff, 0x00}
- }
-
- path := p.Add(image.Pt(
- int(math.Round(float64(-20*dpi))),
- int(math.Round(float64(12*dpi))),
- ))
- c.PathArcTo(path, 6*dpi, 0, 2*math.Pi, -1)
- c.PathStroke(col, false, 2)
- }),
- ),
- ),
- giu.Style().SetDisabled(true).To(
- giu.Button(s("W.I.P")).Size(giu.Auto, 0),
- ),
- ),
- ),
-
- giu.Dummy(0, 3),
- giu.Separator(),
- giu.Dummy(0, 0),
-
- giu.Style().SetDisabled(password == "" || (password != cPassword && mode == "encrypt")).To(
- giu.Label(s(metadataPrompt)),
- giu.Style().SetDisabled(metadataDisabled).To(
- giu.InputText(&metadata).Size(giu.Auto),
- ),
-
- giu.Label(s("Advanced:")),
- giu.Custom(func() {
- if mode != "decrypt" {
- giu.Row(
- giu.Checkbox(s("Use fast mode"), &fast),
- giu.Dummy(-221, 0),
- giu.Checkbox(s("Encode with Reed-Solomon"), &reedsolo),
- ).Build()
- giu.Row(
- giu.Checkbox(s("Use paranoid mode"), ¶noid),
- giu.Dummy(-221, 0),
- giu.Checkbox(s("Delete files when complete"), &deleteWhenDone),
- ).Build()
- giu.Row(
- giu.Style().SetDisabled(!(len(allFiles) > 1 || len(onlyFolders) > 0)).To(
- giu.Checkbox(s("Compress files"), &compress),
- ),
- giu.Dummy(-221, 0),
- giu.Checkbox(s("Split every"), &split),
- giu.InputText(&splitSize).Size(55/dpi).Flags(giu.InputTextFlagsCharsHexadecimal).OnChange(func() {
- split = splitSize != ""
- }),
- giu.Combo("##splitter", splitUnits[splitSelected], splitUnits, &splitSelected).Size(52),
- ).Build()
- } else {
- giu.Checkbox(s("Keep decrypted output even if it's corrupted or modified"), &keep).Build()
- giu.Checkbox(s("Delete the encrypted files after a successful decryption"), &deleteWhenDone).Build()
- }
- }),
-
- giu.Label(s("Save output as:")),
- giu.Custom(func() {
- w, _ := giu.GetAvailableRegion()
- bw, _ := giu.CalcTextSize(s("Change"))
- p, _ := giu.GetWindowPadding()
- bw += p * 2
- dw := w - bw - p
- giu.Style().SetDisabled(true).To(
- giu.InputText(&outputFile).Size(dw / dpi / dpi).Flags(giu.InputTextFlagsReadOnly),
- ).Build()
- giu.SameLine()
- giu.Button(s("Change")).Size(bw/dpi, 0).OnClick(func() {
- file, _ := dialog.File().Title(s("Save as:")).Save()
- if file == "" {
- return
- }
-
- if mode == "encrypt" {
- if len(allFiles) > 1 || len(onlyFolders) > 0 {
- file = strings.TrimSuffix(file, ".zip.pcv")
- file = strings.TrimSuffix(file, ".pcv")
- if !strings.HasSuffix(file, ".zip.pcv") {
- file += ".zip.pcv"
- }
- } else {
- file = strings.TrimSuffix(file, ".pcv")
- ind := strings.Index(inputFile, ".")
- file += inputFile[ind:]
- if !strings.HasSuffix(file, ".pcv") {
- file += ".pcv"
- }
- }
- } else {
- ind := strings.Index(file, ".")
- if ind != -1 {
- file = file[:ind]
- }
- if strings.HasSuffix(inputFile, ".zip.pcv") {
- file += ".zip"
- } else {
- tmp := strings.TrimSuffix(filepath.Base(inputFile), ".pcv")
- tmp = tmp[strings.Index(tmp, "."):]
- file += tmp
- }
- }
-
- outputFile = file
- }).Build()
- giu.Tooltip(s("Save the output with a custom path and name.")).Build()
- }),
-
- giu.Dummy(0, 2),
- giu.Separator(),
- giu.Dummy(0, 3),
-
- giu.Button(s("Start")).Size(giu.Auto, 34).OnClick(func() {
- if keyfile && keyfiles == nil {
- mainStatus = "Please select your keyfiles."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- return
- }
- _, err := os.Stat(outputFile)
- if err == nil {
- showConfirmation = true
- giu.Update()
- } else {
- showProgress = true
- giu.Update()
- go func() {
- work()
- working = false
- showProgress = false
- debug.FreeOSMemory()
- giu.Update()
- }()
- }
- }),
- giu.Style().SetColor(giu.StyleColorText, mainStatusColor).To(
- giu.Label(s(mainStatus)),
- ),
- ),
- ),
- giu.TabItem(s("Checksum")).Layout(
- giu.Custom(func() {
- if giu.IsItemActive() {
- tab = 1
- }
- }),
- giu.Label(s("Toggle the hashes you would like to generate and drop a file here.")),
-
- // MD5
- giu.Custom(func() {
- giu.Checkbox("MD5:", &md5Selected).OnChange(func() {
- csMd5 = ""
- }).Build()
- giu.SameLine()
- w, _ := giu.GetAvailableRegion()
- bw, _ := giu.CalcTextSize(s("Copy"))
- padding, _ := giu.GetWindowPadding()
- bw += 2 * padding
- size := w - bw - padding
- giu.Dummy(size/dpi, 0).Build()
- giu.SameLine()
- giu.Button(s("Copy")+"##md5").Size(bw/dpi, 0).OnClick(func() {
- clipboard.WriteAll(csMd5)
- }).Build()
- }),
- giu.Style().SetColor(giu.StyleColorBorder, md5Color).To(
- giu.Style().SetDisabled(true).To(
- giu.InputText(&csMd5).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly),
- ),
- ),
-
- // SHA1
- giu.Custom(func() {
- giu.Checkbox("SHA1:", &sha1Selected).OnChange(func() {
- csSha1 = ""
- }).Build()
- giu.SameLine()
- w, _ := giu.GetAvailableRegion()
- bw, _ := giu.CalcTextSize(s("Copy"))
- padding, _ := giu.GetWindowPadding()
- bw += 2 * padding
- size := w - bw - padding
- giu.Dummy(size/dpi, 0).Build()
- giu.SameLine()
- giu.Button(s("Copy")+"##sha1").Size(bw/dpi, 0).OnClick(func() {
- clipboard.WriteAll(csSha1)
- }).Build()
- }),
- giu.Style().SetColor(giu.StyleColorBorder, sha1Color).To(
- giu.Style().SetDisabled(true).To(
- giu.InputText(&csSha1).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly),
- ),
- ),
-
- // SHA256
- giu.Custom(func() {
- giu.Checkbox("SHA256:", &sha256Selected).OnChange(func() {
- csSha256 = ""
- }).Build()
- giu.SameLine()
- w, _ := giu.GetAvailableRegion()
- bw, _ := giu.CalcTextSize(s("Copy"))
- padding, _ := giu.GetWindowPadding()
- bw += 2 * padding
- size := w - bw - padding
- giu.Dummy(size/dpi, 0).Build()
- giu.SameLine()
- giu.Button(s("Copy")+"##sha256").Size(bw/dpi, 0).OnClick(func() {
- clipboard.WriteAll(csSha256)
- }).Build()
- }),
- giu.Style().SetColor(giu.StyleColorBorder, sha256Color).To(
- giu.Style().SetDisabled(true).To(
- giu.InputText(&csSha256).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly),
- ),
- ),
-
- // SHA3-256
- giu.Custom(func() {
- giu.Checkbox("SHA3:", &sha3Selected).OnChange(func() {
- csSha3 = ""
- }).Build()
- giu.SameLine()
- w, _ := giu.GetAvailableRegion()
- bw, _ := giu.CalcTextSize(s("Copy"))
- padding, _ := giu.GetWindowPadding()
- bw += 2 * padding
- size := w - bw - padding
- giu.Dummy(size/dpi, 0).Build()
- giu.SameLine()
- giu.Button(s("Copy")+"##sha3").Size(bw/dpi, 0).OnClick(func() {
- clipboard.WriteAll(csSha3)
- }).Build()
- }),
- giu.Style().SetColor(giu.StyleColorBorder, sha3Color).To(
- giu.Style().SetDisabled(true).To(
- giu.InputText(&csSha3).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly),
- ),
- ),
-
- // BLAKE2b
- giu.Custom(func() {
- giu.Checkbox("BLAKE2b:", &blake2bSelected).OnChange(func() {
- csBlake2b = ""
- }).Build()
- giu.SameLine()
- w, _ := giu.GetAvailableRegion()
- bw, _ := giu.CalcTextSize(s("Copy"))
- padding, _ := giu.GetWindowPadding()
- bw += 2 * padding
- size := w - bw - padding
- giu.Dummy(size/dpi, 0).Build()
- giu.SameLine()
- giu.Button(s("Copy")+"##blake2b").Size(bw/dpi, 0).OnClick(func() {
- clipboard.WriteAll(csBlake2b)
- }).Build()
- }),
- giu.Style().SetColor(giu.StyleColorBorder, blake2bColor).To(
- giu.Style().SetDisabled(true).To(
- giu.InputText(&csBlake2b).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly),
- ),
- ),
-
- // BLAKE2s
- giu.Custom(func() {
- giu.Checkbox("BLAKE2s:", &blake2sSelected).OnChange(func() {
- csBlake2s = ""
- }).Build()
- giu.SameLine()
- w, _ := giu.GetAvailableRegion()
- bw, _ := giu.CalcTextSize(s("Copy"))
- padding, _ := giu.GetWindowPadding()
- bw += 2 * padding
- size := w - bw - padding
- giu.Dummy(size/dpi, 0).Build()
- giu.SameLine()
- giu.Button(s("Copy")+"##blake2s").Size(bw/dpi, 0).OnClick(func() {
- clipboard.WriteAll(csBlake2s)
- }).Build()
- }),
- giu.Style().SetColor(giu.StyleColorBorder, blake2sColor).To(
- giu.Style().SetDisabled(true).To(
- giu.InputText(&csBlake2s).Size(giu.Auto).Flags(giu.InputTextFlagsReadOnly),
- ),
- ),
-
- // Input entry for validating a checksum
- giu.Row(
- giu.Label(s("Validate a checksum:")),
- giu.Custom(func() {
- bw, _ := giu.CalcTextSize(s("Paste"))
- padding, _ := giu.GetWindowPadding()
- bw += 2 * padding
- giu.Button(s("Paste")).Size(bw/dpi, 0).OnClick(func() {
- tmp, _ := clipboard.ReadAll()
- csValidate = tmp
- md5Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha1Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
- blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
- if csValidate == "" {
- return
- }
- csValidate = strings.ToLower(csValidate)
- if csValidate == csMd5 {
- md5Color = color.RGBA{0x00, 0xff, 0x00, 0xff}
- } else if csValidate == csSha1 {
- sha1Color = color.RGBA{0x00, 0xff, 0x00, 0xff}
- } else if csValidate == csSha256 {
- sha256Color = color.RGBA{0x00, 0xff, 0x00, 0xff}
- } else if csValidate == csSha3 {
- sha3Color = color.RGBA{0x00, 0xff, 0x00, 0xff}
- } else if csValidate == csBlake2b {
- blake2bColor = color.RGBA{0x00, 0xff, 0x00, 0xff}
- } else if csValidate == csBlake2s {
- blake2sColor = color.RGBA{0x00, 0xff, 0x00, 0xff}
- }
- giu.Update()
- }).Build()
- }),
- giu.Custom(func() {
- bw, _ := giu.CalcTextSize(s("Paste"))
- padding, _ := giu.GetWindowPadding()
- bw += 2 * padding
- giu.Button(s("Clear")).Size(bw/dpi, 0).OnClick(func() {
- csValidate = ""
- md5Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha1Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
- blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
- }).Build()
- }),
- ),
- giu.Style().SetDisabled(true).To(
- giu.InputText(&csValidate).Size(giu.Auto),
- ),
-
- // Progress bar
- giu.Label(s("Progress:")),
- giu.ProgressBar(csProgress).Size(giu.Auto, 0),
- ),
- giu.TabItem(s("About")).Layout(
- giu.Custom(func() {
- if giu.IsItemActive() {
- tab = 2
- }
- }),
- giu.Label(fmt.Sprintf(s("Picocrypt %s, created by Evan Su (https://evansu.cc/)."), version)),
- giu.Label(s("Released under a GNU GPL v3 License.")),
- giu.Label(s("A warm thank you to all the people listed below.")),
- giu.Label(s("Patrons:")),
- giu.Label(" - Frederick Doe"),
- giu.Label(s("Donators:")),
- giu.Label(" - jp26"),
- giu.Label(" - W.Graham"),
- giu.Label(" - N. Chin"),
- giu.Label(" - Manjot"),
- giu.Label(" - Phil P."),
- giu.Label(" - E. Zahard"),
- giu.Label(s("Translators:")),
- giu.Label("umitseyhan75, digitalblossom, zeeaall, francirc, kurpau"),
- giu.Label(s("Other:")),
- giu.Label("Fuderal, u/greenreddits, u/Tall_Escape, u/NSABackdoors"),
- ),
- ).Build()
- }),
- giu.Custom(func() {
- if !windowOptimized || windowOptimized {
- windowOptimized = true
- var pad int
- if tab == 1 {
- pad = 6
- }
- if tab == 2 {
- pad = 1
- }
- if tab == 3 {
- pad = 2
- }
- window.SetSize(int(442*dpi), giu.GetCursorPos().Y+1+pad)
- }
- }),
- )
-}
-
-func onDrop(names []string) {
- if tab == 1 {
- go generateChecksums(names[0])
- return
- }
- if tab == 2 {
- return
- }
-
- if showKeyfile {
- keyfiles = append(keyfiles, names...)
- var tmp []string
- for _, i := range keyfiles {
- duplicate := false
- for _, j := range tmp {
- if i == j {
- duplicate = true
- }
- }
- stat, _ := os.Stat(i)
- if !duplicate && !stat.IsDir() {
- tmp = append(tmp, i)
- }
- }
- keyfiles = tmp
- if len(keyfiles) == 1 {
- keyfilePrompt = s("Using 1 keyfile.")
- } else {
- keyfilePrompt = fmt.Sprintf(s("Using %d keyfiles."), len(keyfiles))
- }
- return
- }
-
- // Clear variables
- recombine = false
- onlyFiles = nil
- onlyFolders = nil
- allFiles = nil
- files, folders := 0, 0
- resetUI()
-
- if len(names) == 1 {
- stat, _ := os.Stat(names[0])
- if stat.IsDir() {
- // Update variables
- mode = "encrypt"
- folders++
- inputLabel = s("1 folder selected.")
-
- // Add the folder
- onlyFolders = append(onlyFolders, names[0])
-
- // Set the input and output paths
- inputFile = filepath.Join(filepath.Dir(names[0]), s("Encrypted")) + ".zip"
- outputFile = filepath.Join(filepath.Dir(names[0]), s("Encrypted")) + ".zip.pcv"
- } else {
- files++
- name := filepath.Base(names[0])
- nums := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
- endsNum := false
- for _, i := range nums {
- if strings.HasSuffix(names[0], i) {
- endsNum = true
- }
- }
- isSplit := strings.Contains(names[0], ".pcv.") && endsNum
-
- // Decide if encrypting or decrypting
- if strings.HasSuffix(names[0], ".pcv") || isSplit {
- //var err error
- mode = "decrypt"
- inputLabel = name + s(" (will decrypt)")
- metadataPrompt = s("Metadata (read-only):")
- metadataDisabled = true
-
- if isSplit {
- inputLabel = name + s(" (will recombine and decrypt)")
- ind := strings.Index(names[0], ".pcv")
- names[0] = names[0][:ind+4]
- inputFile = names[0]
- outputFile = names[0][:ind]
- recombine = true
- } else {
- outputFile = names[0][:len(names[0])-4]
- }
-
- // Open input file in read-only mode
- var fin *os.File
- if isSplit {
- fin, _ = os.Open(names[0] + ".0")
- } else {
- fin, _ = os.Open(names[0])
- }
-
- // Use regex to test if input is a valid Picocrypt volume
- tmp := make([]byte, 30)
- fin.Read(tmp)
- if string(tmp[:5]) == "v1.13" {
- resetUI()
- mainStatus = "Please use Picocrypt v1.13 to decrypt this file."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- fin.Close()
- return
- }
- if valid, _ := regexp.Match(`^v\d\.\d{2}.{10}0?\d+`, tmp); !valid && !isSplit {
- resetUI()
- mainStatus = "This doesn't seem to be a Picocrypt volume."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- fin.Close()
- return
- }
- fin.Seek(0, 0)
-
- // Read metadata and insert into box
- var err error
- tmp = make([]byte, 15)
- fin.Read(tmp)
- tmp, _ = rsDecode(rs5, tmp)
- if string(tmp) == "v1.14" || string(tmp) == "v1.15" || string(tmp) == "v1.16" {
- resetUI()
- mainStatus = "Please use Picocrypt v1.16 to decrypt this file."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- fin.Close()
- return
- }
- tmp = make([]byte, 15)
- fin.Read(tmp)
- tmp, err = rsDecode(rs5, tmp)
-
- if err == nil {
- metadataLength, _ := strconv.Atoi(string(tmp))
- tmp = make([]byte, metadataLength*3)
- fin.Read(tmp)
- metadata = ""
-
- for i := 0; i < metadataLength*3; i += 3 {
- t, err := rsDecode(rs1, tmp[i:i+3])
- if err != nil {
- metadata = s("Metadata is corrupted.")
- break
- }
- metadata += string(t)
- }
- } else {
- metadata = s("Metadata is corrupted.")
- }
-
- flags := make([]byte, 18)
- fin.Read(flags)
- fin.Close()
- flags, err = rsDecode(rs6, flags)
- if err != nil {
- mainStatus = "Input file is corrupt and cannot be decrypted."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- return
- }
-
- if flags[2] == 1 {
- keyfile = true
- keyfilePrompt = s("Keyfiles required.")
- } else {
- keyfilePrompt = s("Not applicable.")
- }
- if flags[5] == 1 {
- keyfileOrderMatters = true
- }
- } else {
- mode = "encrypt"
- inputLabel = name + s(" (will encrypt)")
- inputFile = names[0]
- outputFile = names[0] + ".pcv"
- }
-
- // Add the file
- onlyFiles = append(onlyFiles, names[0])
- inputFile = names[0]
- }
- } else {
- mode = "encrypt"
-
- // There are multiple dropped items, check each one
- for _, name := range names {
- stat, _ := os.Stat(name)
-
- // Check if item is a file or a directory
- if stat.IsDir() {
- folders++
- onlyFolders = append(onlyFolders, name)
- } else {
- files++
- onlyFiles = append(onlyFiles, name)
- allFiles = append(allFiles, name)
- }
- }
-
- if folders == 0 {
- inputLabel = fmt.Sprintf(s("%d files selected."), files)
- } else if files == 0 {
- inputLabel = fmt.Sprintf(s("%d folders selected."), files)
- } else {
- if files == 1 && folders > 1 {
- inputLabel = fmt.Sprintf(s("1 file and %d folders selected."), folders)
- } else if folders == 1 && files > 1 {
- inputLabel = fmt.Sprintf(s("%d files and 1 folder selected."), files)
- } else if folders == 1 && files == 1 {
- inputLabel = s("1 file and 1 folder selected.")
- } else {
- inputLabel = fmt.Sprintf(s("%d files and %d folders selected."), files, folders)
- }
- }
-
- // Set the input and output paths
- inputFile = filepath.Join(filepath.Dir(names[0]), s("Encrypted")) + ".zip"
- outputFile = filepath.Join(filepath.Dir(names[0]), s("Encrypted")) + ".zip.pcv"
- }
- // Recursively add all files to 'allFiles'
- if folders > 0 {
- for _, name := range onlyFolders {
- filepath.Walk(name, func(path string, _ os.FileInfo, _ error) error {
- stat, _ := os.Stat(path)
- if !stat.IsDir() {
- allFiles = append(allFiles, path)
- }
- return nil
- })
- }
- }
-}
-
-func work() {
- popupStatus = s("Starting...")
- mainStatus = "Working..."
- mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
- working = true
- padded := false
-
- var salt []byte
- var hkdfSalt []byte
- var serpentSalt []byte
- var nonce []byte
- var keyHash []byte
- var _keyHash []byte
- var keyfileKey []byte
- var keyfileHash []byte = make([]byte, 32)
- var _keyfileHash []byte
- var dataMac []byte
-
- if mode == "encrypt" {
- if compress {
- popupStatus = s("Compressing files...")
- } else {
- popupStatus = s("Combining files...")
- }
-
- // "Tar" files together (a .zip file with no compression)
- if len(allFiles) > 1 || len(onlyFolders) > 0 {
- var rootDir string
- if len(onlyFolders) > 0 {
- rootDir = filepath.Dir(onlyFolders[0])
- } else {
- rootDir = filepath.Dir(onlyFiles[0])
- }
-
- file, err := os.Create(inputFile)
- if err != nil {
- mainStatus = "Access denied by operating system."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- return
- }
-
- w := zip.NewWriter(file)
- for i, path := range allFiles {
- if !working {
- w.Close()
- file.Close()
- os.Remove(inputFile)
- mainStatus = "Operation cancelled by user."
- mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
- return
- }
- progressInfo = fmt.Sprintf("%d/%d", i, len(allFiles))
- progress = float32(i) / float32(len(allFiles))
- giu.Update()
- if path == inputFile {
- continue
- }
-
- stat, _ := os.Stat(path)
- header, _ := zip.FileInfoHeader(stat)
- header.Name = strings.TrimPrefix(path, rootDir)
- header.Name = filepath.ToSlash(header.Name)
- header.Name = strings.TrimPrefix(header.Name, "/")
-
- if compress {
- header.Method = zip.Deflate
- } else {
- header.Method = zip.Store
- }
- writer, _ := w.CreateHeader(header)
- file, _ := os.Open(path)
- io.Copy(writer, file)
- file.Close()
- }
- w.Flush()
- w.Close()
- file.Close()
- }
- }
-
- if recombine {
- popupStatus = s("Recombining file...")
- total := 0
-
- for {
- _, err := os.Stat(fmt.Sprintf("%s.%d", inputFile, total))
- if err != nil {
- break
- }
- total++
- }
-
- fout, _ := os.Create(inputFile)
- for i := 0; i < total; i++ {
- fin, _ := os.Open(fmt.Sprintf("%s.%d", inputFile, i))
- for {
- data := make([]byte, 1048576)
- read, err := fin.Read(data)
- if err != nil {
- break
- }
- data = data[:read]
- fout.Write(data)
- }
- fin.Close()
- progressInfo = fmt.Sprintf("%d/%d", i, total)
- progress = float32(i) / float32(total)
- giu.Update()
- }
- fout.Close()
- progressInfo = ""
- }
-
- stat, _ := os.Stat(inputFile)
- total := stat.Size()
- if mode == "decrypt" {
- total -= 789
- }
-
- // XChaCha20's max message size is 256 GiB
- if total > 256*1073741824 {
- mainStatus = "Total size is larger than 256 GiB, XChaCha20's limit."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- return
- }
-
- // Open input file in read-only mode
- fin, err := os.Open(inputFile)
- if err != nil {
- mainStatus = "Access denied by operating system."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- return
- }
-
- var fout *os.File
-
- // If encrypting, generate values; if decrypting, read values from file
- if mode == "encrypt" {
- popupStatus = s("Generating values...")
- giu.Update()
-
- var err error
- fout, err = os.Create(outputFile)
- if err != nil {
- mainStatus = "Access denied by operating system."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- return
- }
-
- // Generate random cryptography values
- salt = make([]byte, 16)
- hkdfSalt = make([]byte, 32)
- serpentSalt = make([]byte, 16)
- nonce = make([]byte, 24)
-
- // Write version to file
- fout.Write(rsEncode(rs5, []byte(version)))
-
- // Encode the length of the metadata with Reed-Solomon
- metadataLength := []byte(fmt.Sprintf("%05d", len(metadata)))
- metadataLength = rsEncode(rs5, metadataLength)
-
- // Write the length of the metadata to file
- fout.Write(metadataLength)
-
- // Reed-Solomon-encode the metadata and write to file
- for _, i := range []byte(metadata) {
- fout.Write(rsEncode(rs1, []byte{i}))
- }
-
- flags := make([]byte, 6)
- if fast {
- flags[0] = 1
- }
- if paranoid {
- flags[1] = 1
- }
- if len(keyfiles) > 0 {
- flags[2] = 1
- }
- if reedsolo {
- flags[3] = 1
- }
- if total%1048576 >= 1048448 {
- flags[4] = 1
- }
- if keyfileOrderMatters {
- flags[5] = 1
- }
- flags = rsEncode(rs6, flags)
- fout.Write(flags)
-
- // Fill salts and nonce with Go's CSPRNG
- rand.Read(salt)
- rand.Read(hkdfSalt)
- rand.Read(serpentSalt)
- rand.Read(nonce)
-
- // Encode salt with Reed-Solomon and write to file
- _salt := rsEncode(rs16, salt)
- fout.Write(_salt)
-
- // Encode HKDF salt with Reed-Solomon and write to file
- _hkdfSalt := rsEncode(rs32, hkdfSalt)
- fout.Write(_hkdfSalt)
-
- // Encode Serpent salt with Reed-Solomon and write to file
- _serpentSalt := rsEncode(rs16, serpentSalt)
- fout.Write(_serpentSalt)
-
- // Encode nonce with Reed-Solomon and write to file
- _nonce := rsEncode(rs24, nonce)
- fout.Write(_nonce)
-
- // Write placeholder for hash of key
- fout.Write(make([]byte, 192))
-
- // Write placeholder for hash of hash of keyfile
- fout.Write(make([]byte, 96))
-
- // Write placeholder for HMAC-BLAKE2b/HMAC-SHA3 of file
- fout.Write(make([]byte, 192))
- } else {
- var err1 error
- var err2 error
- var err3 error
- var err4 error
- var err5 error
- var err6 error
- var err7 error
- var err8 error
- var err9 error
- var err10 error
-
- popupStatus = s("Reading values...")
- giu.Update()
-
- version := make([]byte, 15)
- fin.Read(version)
- _, err1 = rsDecode(rs5, version)
-
- tmp := make([]byte, 15)
- fin.Read(tmp)
- tmp, err2 = rsDecode(rs5, tmp)
- metadataLength, _ := strconv.Atoi(string(tmp))
-
- fin.Read(make([]byte, metadataLength*3))
-
- flags := make([]byte, 18)
- fin.Read(flags)
- flags, err3 = rsDecode(rs6, flags)
- fast = flags[0] == 1
- paranoid = flags[1] == 1
- reedsolo = flags[3] == 1
- padded = flags[4] == 1
-
- salt = make([]byte, 48)
- fin.Read(salt)
- salt, err4 = rsDecode(rs16, salt)
-
- hkdfSalt = make([]byte, 96)
- fin.Read(hkdfSalt)
- hkdfSalt, err5 = rsDecode(rs32, hkdfSalt)
-
- serpentSalt = make([]byte, 48)
- fin.Read(serpentSalt)
- serpentSalt, err6 = rsDecode(rs16, serpentSalt)
-
- nonce = make([]byte, 72)
- fin.Read(nonce)
- nonce, err7 = rsDecode(rs24, nonce)
-
- _keyHash = make([]byte, 192)
- fin.Read(_keyHash)
- _keyHash, err8 = rsDecode(rs64, _keyHash)
-
- _keyfileHash = make([]byte, 96)
- fin.Read(_keyfileHash)
- _keyfileHash, err9 = rsDecode(rs32, _keyfileHash)
-
- dataMac = make([]byte, 192)
- fin.Read(dataMac)
- dataMac, err10 = rsDecode(rs64, dataMac)
-
- // Is there a better way?
- if err1 != nil || err2 != nil || err3 != nil || err4 != nil || err5 != nil ||
- err6 != nil || err7 != nil || err8 != nil || err9 != nil || err10 != nil {
- if keep {
- kept = true
- } else {
- mainStatus = "The header is corrupt and the input file cannot be decrypted."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- fin.Close()
- return
- }
- }
- }
-
- popupStatus = s("Deriving key...")
- progress = 0
- progressInfo = ""
- giu.Update()
-
- // Derive encryption/decryption keys and subkeys
- var key []byte
- if fast {
- key = argon2.IDKey(
- []byte(password),
- salt,
- 4,
- 131072,
- 4,
- 32,
- )
- } else if paranoid {
- key = argon2.IDKey(
- []byte(password),
- salt,
- 8,
- 1048576,
- 8,
- 32,
- )
- } else {
- key = argon2.IDKey(
- []byte(password),
- salt,
- 4,
- 1048576,
- 4,
- 32,
- )
- }
-
- if !working {
- mainStatus = "Operation cancelled by user."
- mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
- if mode == "encrypt" && (len(allFiles) > 1 || len(onlyFolders) > 0) {
- os.Remove(outputFile)
- }
- if recombine {
- os.Remove(inputFile)
- }
- os.Remove(outputFile)
- return
- }
-
- if len(keyfiles) > 0 || keyfile {
- if keyfileOrderMatters {
- var keysum = sha3.New256()
- for _, path := range keyfiles {
- kin, _ := os.Open(path)
- kstat, _ := os.Stat(path)
- kbytes := make([]byte, kstat.Size())
- kin.Read(kbytes)
- kin.Close()
- keysum.Write(kbytes)
- }
- keyfileKey = keysum.Sum(nil)
- keyfileSha3 := sha3.New256()
- keyfileSha3.Write(keyfileKey)
- keyfileHash = keyfileSha3.Sum(nil)
- } else {
- var keysum []byte
- for _, path := range keyfiles {
- kin, _ := os.Open(path)
- kstat, _ := os.Stat(path)
- kbytes := make([]byte, kstat.Size())
- kin.Read(kbytes)
- kin.Close()
- ksha3 := sha3.New256()
- ksha3.Write(kbytes)
- keyfileKey := ksha3.Sum(nil)
- if keysum == nil {
- keysum = keyfileKey
- } else {
- for i, j := range keyfileKey {
- keysum[i] ^= j
- }
- }
- }
- keyfileKey = keysum
- keyfileSha3 := sha3.New256()
- keyfileSha3.Write(keysum)
- keyfileHash = keyfileSha3.Sum(nil)
- }
- }
-
- sha3_512 := sha3.New512()
- sha3_512.Write(key)
- keyHash = sha3_512.Sum(nil)
-
- // Validate password and/or keyfiles
- if mode == "decrypt" {
- keyCorrect := true
- keyfileCorrect := true
- var tmp bool
-
- keyCorrect = !(subtle.ConstantTimeCompare(keyHash, _keyHash) == 0)
- if keyfile {
- keyfileCorrect = !(subtle.ConstantTimeCompare(keyfileHash, _keyfileHash) == 0)
- tmp = !keyCorrect || !keyfileCorrect
- } else {
- tmp = !keyCorrect
- }
-
- if tmp || keep {
- if keep {
- kept = true
- } else {
- fin.Close()
- if !keyCorrect {
- mainStatus = "The provided password is incorrect."
- } else {
- if keyfileOrderMatters {
- mainStatus = "Incorrect keyfiles and/or order."
- } else {
- mainStatus = "Incorrect keyfiles."
- }
- }
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- key = nil
- if recombine {
- os.Remove(inputFile)
- }
- return
- }
- }
-
- var err error
- fout, err = os.Create(outputFile)
- if err != nil {
- mainStatus = "Access denied by operating system."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- return
- }
- }
-
- if len(keyfiles) > 0 || keyfile {
- // XOR key and keyfile
- tmp := key
- key = make([]byte, 32)
- for i := range key {
- key[i] = tmp[i] ^ keyfileKey[i]
- }
- }
-
- done := 0
- counter := 0
- startTime := time.Now()
- chacha20, _ := chacha20.NewUnauthenticatedCipher(key, nonce)
-
- // Use HKDF-SHA3 to generate a subkey
- var mac hash.Hash
- subkey := make([]byte, 32)
- hkdf := hkdf.New(sha3.New256, key, hkdfSalt, nil)
- hkdf.Read(subkey)
- if fast {
- // Keyed BLAKE2b
- mac, _ = blake2b.New512(subkey)
- } else {
- // HMAC-SHA3
- mac = hmac.New(sha3.New512, subkey)
- }
-
- // Generate another subkey and cipher (not used unless paranoid mode is checked)
- serpentKey := make([]byte, 32)
- hkdf.Read(serpentKey)
- _serpent, _ := serpent.NewCipher(serpentKey)
- serpentCTR := cipher.NewCTR(_serpent, serpentSalt)
-
- for {
- if !working {
- mainStatus = "Operation cancelled by user."
- mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
- fin.Close()
- fout.Close()
- if mode == "encrypt" && (len(allFiles) > 1 || len(onlyFolders) > 0) {
- os.Remove(outputFile)
- }
- if recombine {
- os.Remove(inputFile)
- }
- os.Remove(outputFile)
- return
- }
-
- var data []byte
- if mode == "decrypt" && reedsolo {
- data = make([]byte, 1114112)
- } else {
- data = make([]byte, 1048576)
- }
-
- size, err := fin.Read(data)
- if err != nil {
- break
- }
- data = data[:size]
- _data := make([]byte, len(data))
-
- // "Actual" encryption is done in the next couple of lines
- if mode == "encrypt" {
- if paranoid {
- serpentCTR.XORKeyStream(_data, data)
- copy(data, _data)
- }
-
- chacha20.XORKeyStream(_data, data)
- mac.Write(_data)
-
- if reedsolo {
- copy(data, _data)
- _data = nil
- if len(data) == 1048576 {
- for i := 0; i < 1048576; i += 128 {
- tmp := data[i : i+128]
- tmp = rsEncode(rs128, tmp)
- _data = append(_data, tmp...)
- }
- } else {
- chunks := math.Floor(float64(len(data)) / 128)
- for i := 0; float64(i) < chunks; i++ {
- tmp := data[i*128 : (i+1)*128]
- tmp = rsEncode(rs128, tmp)
- _data = append(_data, tmp...)
- }
- tmp := data[int(chunks*128):]
- _data = append(_data, rsEncode(rs128, pad(tmp))...)
- }
- }
- } else {
- if reedsolo {
- copy(_data, data)
- data = nil
- if len(_data) == 1114112 {
- for i := 0; i < 1114112; i += 136 {
- tmp := _data[i : i+136]
- tmp, err = rsDecode(rs128, tmp)
- if err != nil {
- if keep {
- kept = true
- } else {
- mainStatus = "The input file is too corrupted to decrypt."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- fin.Close()
- fout.Close()
- broken()
- return
- }
- }
- if i == 1113976 && done+1114112 >= int(total) && padded {
- tmp = unpad(tmp)
- }
- data = append(data, tmp...)
- }
- } else {
- chunks := len(_data)/136 - 1
- for i := 0; i < chunks; i++ {
- tmp := _data[i*136 : (i+1)*136]
- tmp, err = rsDecode(rs128, tmp)
- if err != nil {
- if keep {
- kept = true
- } else {
- mainStatus = "The input file is too corrupted to decrypt."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- fin.Close()
- fout.Close()
- broken()
- return
- }
- }
- data = append(data, tmp...)
- }
- tmp := _data[int(chunks)*136:]
- tmp, err = rsDecode(rs128, tmp)
- if err != nil {
- if keep {
- kept = true
- } else {
- mainStatus = "The input file is too corrupted to decrypt."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- fin.Close()
- fout.Close()
- broken()
- return
- }
- }
- tmp = unpad(tmp)
- data = append(data, tmp...)
- }
- _data = make([]byte, len(data))
- }
-
- mac.Write(data)
- chacha20.XORKeyStream(_data, data)
-
- if paranoid {
- copy(data, _data)
- serpentCTR.XORKeyStream(_data, data)
- }
- }
- fout.Write(_data)
-
- // Update stats
- if mode == "decrypt" && reedsolo {
- done += 1114112
- } else {
- done += 1048576
- }
- counter++
- progress = float32(done) / float32(total)
- elapsed := float64(time.Since(startTime)) / math.Pow(10, 9)
- speed := float64(done) / elapsed / math.Pow(10, 6)
- eta := int(math.Floor(float64(total-int64(done)) / (speed * math.Pow(10, 6))))
-
- if progress > 1 {
- progress = 1
- }
-
- progressInfo = fmt.Sprintf("%.2f%%", progress*100)
- popupStatus = fmt.Sprintf(s("Working at %.2f MB/s (ETA: %s)"), speed, humanize(eta))
- giu.Update()
- }
-
- if mode == "encrypt" {
- // Seek back to header and write important data
- fout.Seek(int64(312+len(metadata)*3), 0)
- fout.Write(rsEncode(rs64, keyHash))
- fout.Write(rsEncode(rs32, keyfileHash))
- fout.Write(rsEncode(rs64, mac.Sum(nil)))
- } else {
- // Validate the authenticity of decrypted data
- if subtle.ConstantTimeCompare(mac.Sum(nil), dataMac) == 0 {
- if keep {
- kept = true
- } else {
- fin.Close()
- fout.Close()
- broken()
- return
- }
- }
- }
-
- fin.Close()
- fout.Close()
-
- // Split files into chunks
- if split {
- var splitted []string
- popupStatus = s("Splitting file...")
- stat, _ := os.Stat(outputFile)
- size := stat.Size()
- finished := 0
- chunkSize, _ := strconv.Atoi(splitSize)
-
- // User can choose KiB, MiB, and GiB
- if splitSelected == 0 {
- chunkSize *= 1024
- } else if splitSelected == 1 {
- chunkSize *= 1048576
- } else {
- chunkSize *= 1073741824
- }
- chunks := int(math.Ceil(float64(size) / float64(chunkSize)))
- fin, _ := os.Open(outputFile)
-
- for i := 0; i < chunks; i++ {
- fout, _ := os.Create(fmt.Sprintf("%s.%d", outputFile, i))
- done := 0
- for {
- data := make([]byte, 1048576)
- read, err := fin.Read(data)
- if err != nil {
- break
- }
- if !working {
- fin.Close()
- fout.Close()
- mainStatus = "Operation cancelled by user."
- mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
-
- // If user cancels, remove the unfinished files
- for _, j := range splitted {
- os.Remove(j)
- }
- os.Remove(fmt.Sprintf("%s.%d", outputFile, i))
- os.Remove(outputFile)
- return
- }
- data = data[:read]
- fout.Write(data)
- done += read
- if done >= chunkSize {
- break
- }
- }
- fout.Close()
- finished++
- splitted = append(splitted, fmt.Sprintf("%s.%d", outputFile, i))
- progress = float32(finished) / float32(chunks)
- progressInfo = fmt.Sprintf("%d/%d", finished, chunks)
- giu.Update()
- }
- fin.Close()
- os.Remove(outputFile)
- }
-
- // Remove the temporary file used to combine a splitted Picocrypt volume
- if recombine {
- os.Remove(inputFile)
- }
-
- // Delete the temporary zip file if user wishes
- if len(allFiles) > 1 || len(onlyFolders) > 0 {
- os.Remove(inputFile)
- }
-
- if deleteWhenDone {
- progressInfo = ""
- popupStatus = s("Deleted files...")
- giu.Update()
- if mode == "decrypt" {
- if recombine {
- total := 0
- for {
- _, err := os.Stat(fmt.Sprintf("%s.%d", inputFile, total))
- if err != nil {
- break
- }
- os.Remove(fmt.Sprintf("%s.%d", inputFile, total))
- total++
- }
- } else {
- os.Remove(inputFile)
- }
- } else {
- for _, i := range onlyFiles {
- os.Remove(i)
- }
- for _, i := range onlyFolders {
- os.RemoveAll(i)
- }
- }
- }
-
- resetUI()
-
- // If user chose to keep a corrupted/modified file, let them know
- if kept {
- mainStatus = "The input file is corrupted and/or modified. Please be careful."
- mainStatusColor = color.RGBA{0xff, 0xff, 0x00, 0xff}
- } else {
- mainStatus = "Completed."
- mainStatusColor = color.RGBA{0x00, 0xff, 0x00, 0xff}
- }
-
- // Clear UI state
- working = false
- kept = false
- key = nil
- popupStatus = s("Ready.")
-}
-
-// This function is run if an issue occurs during decryption
-func broken() {
- mainStatus = "The input file is either corrupted or intentionally modified."
- mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
- if recombine {
- os.Remove(inputFile)
- }
- os.Remove(outputFile)
- giu.Update()
-}
-
-// Generate file checksums
-func generateChecksums(file string) {
- fin, _ := os.Open(file)
-
- // Clear UI state
- csMd5 = ""
- csSha1 = ""
- csSha256 = ""
- csSha3 = ""
- csBlake2b = ""
- csBlake2s = ""
- md5Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha1Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
- blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
- blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
- csValidate = ""
-
- if md5Selected {
- csMd5 = s("Calculating...")
- }
- if sha1Selected {
- csSha1 = s("Calculating...")
- }
- if sha256Selected {
- csSha256 = s("Calculating...")
- }
- if sha3Selected {
- csSha3 = s("Calculating...")
- }
- if blake2bSelected {
- csBlake2b = s("Calculating...")
- }
- if blake2sSelected {
- csBlake2s = s("Calculating...")
- }
-
- // Create the checksum objects
- crcMd5 := md5.New()
- crcSha1 := sha1.New()
- crcSha256 := sha256.New()
- crcSha3 := sha3.New256()
- crcBlake2b, _ := blake2b.New256(nil)
- crcBlake2s, _ := blake2s.New256(nil)
-
- stat, _ := os.Stat(file)
- total := stat.Size()
- var done int64 = 0
- for {
- var data []byte
- _data := make([]byte, 1048576)
- size, err := fin.Read(_data)
- if err != nil {
- break
- }
- data = _data[:size]
-
- if md5Selected {
- crcMd5.Write(data)
- }
- if sha1Selected {
- crcSha1.Write(data)
- }
- if sha256Selected {
- crcSha256.Write(data)
- }
- if sha3Selected {
- crcSha3.Write(data)
- }
- if blake2bSelected {
- crcBlake2b.Write(data)
- }
- if blake2sSelected {
- crcBlake2s.Write(data)
- }
-
- done += int64(size)
- csProgress = float32(done) / float32(total)
- giu.Update()
- }
- csProgress = 0
- if md5Selected {
- csMd5 = hex.EncodeToString(crcMd5.Sum(nil))
- }
- if sha1Selected {
- csSha1 = hex.EncodeToString(crcSha1.Sum(nil))
- }
- if sha256Selected {
- csSha256 = hex.EncodeToString(crcSha256.Sum(nil))
- }
- if sha3Selected {
- csSha3 = hex.EncodeToString(crcSha3.Sum(nil))
- }
- if blake2bSelected {
- csBlake2b = hex.EncodeToString(crcBlake2b.Sum(nil))
- }
- if blake2sSelected {
- csBlake2s = hex.EncodeToString(crcBlake2s.Sum(nil))
- }
-
- fin.Close()
- giu.Update()
-}
-
-// Reset the UI to a clean state with nothing selected or checked
-func resetUI() {
- mode = ""
- onlyFiles = nil
- onlyFolders = nil
- allFiles = nil
- inputLabel = s("Drop files and folders into this window.")
- password = ""
- cPassword = ""
- keyfiles = nil
- keyfile = false
- keyfileOrderMatters = false
- keyfilePrompt = s("None selected.")
- metadata = ""
- metadataPrompt = "Metadata:"
- metadataDisabled = false
- keep = false
- reedsolo = false
- split = false
- splitSize = ""
- splitSelected = 1
- fast = false
- deleteWhenDone = false
- paranoid = false
- compress = false
- inputFile = ""
- outputFile = ""
- progress = 0
- progressInfo = ""
- mainStatus = "Ready."
- mainStatusColor = color.RGBA{0xff, 0xff, 0xff, 0xff}
- giu.Update()
-}
-
-// Reed-Solomon encoder
-func rsEncode(rs *infectious.FEC, data []byte) []byte {
- var res []byte
- rs.Encode(data, func(s infectious.Share) {
- res = append(res, s.DeepCopy().Data[0])
- })
- return res
-}
-
-// Reed-Solomon decoder
-func rsDecode(rs *infectious.FEC, data []byte) ([]byte, error) {
- tmp := make([]infectious.Share, rs.Total())
- for i := 0; i < rs.Total(); i++ {
- tmp[i] = infectious.Share{
- Number: i,
- Data: []byte{data[i]},
- }
- }
- res, err := rs.Decode(nil, tmp)
- if err != nil {
- if rs.Total() == 136 {
- return data[:128], err
- }
- return data[:rs.Total()/3], err
- }
- return res, nil
-}
-
-// PKCS7 Pad (for use with Reed-Solomon, not for cryptographic purposes)
-func pad(data []byte) []byte {
- padLen := 128 - len(data)%128
- padding := bytes.Repeat([]byte{byte(padLen)}, padLen)
- return append(data, padding...)
-}
-
-// PKCS7 Unpad
-func unpad(data []byte) []byte {
- length := len(data)
- padLen := int(data[length-1])
- return data[:length-padLen]
-}
-
-func genPassword() string {
- chars := ""
- if genpassUpper {
- chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- }
- if genpassLower {
- chars += "abcdefghijklmnopqrstuvwxyz"
- }
- if genpassNums {
- chars += "1234567890"
- }
- if genpassSymbols {
- chars += "-=!@#$^&()_+?"
- }
- if chars == "" {
- return chars
- }
- tmp := make([]byte, genpassLength)
- for i := 0; i < int(genpassLength); i++ {
- j, _ := rand.Int(rand.Reader, new(big.Int).SetUint64(uint64(len(chars))))
- tmp[i] = chars[j.Int64()]
- }
- if genpassCopy {
- clipboard.WriteAll(string(tmp))
- }
- return string(tmp)
-}
-
-// Convert seconds to HH:MM:SS
-func humanize(seconds int) string {
- hours := int(math.Floor(float64(seconds) / 3600))
- seconds %= 3600
- minutes := int(math.Floor(float64(seconds) / 60))
- seconds %= 60
- hours = int(math.Max(float64(hours), 0))
- minutes = int(math.Max(float64(minutes), 0))
- seconds = int(math.Max(float64(seconds), 0))
- return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
-}
-
-func s(term string) string {
- for _, i := range locales {
- if i.iso == selectedLocale {
- for _, j := range locales {
- if j.iso == "en" {
- for k, l := range j.data {
- if l == term {
- return i.data[k]
- }
- }
- }
- }
-
- }
- }
- return term
-}
-
-func main() {
- // Parse locales
- var obj map[string]json.RawMessage
- json.Unmarshal(localeBytes, &obj)
- for i := range obj {
- var tmp []string
- json.Unmarshal(obj[i], &tmp)
- locales = append(locales, locale{
- iso: i,
- data: tmp,
- })
- }
-
- // Check system locale
- for _, i := range locales {
- tmp, err := jibber_jabber.DetectIETF()
- if err == nil {
- if strings.HasPrefix(tmp, i.iso) {
- selectedLocale = i.iso
- for j, k := range allLocales {
- if k == i.iso {
- languageSelected = int32(j)
- }
- }
- }
- }
- }
-
- // Set a universal font
- giu.SetDefaultFontFromBytes(font, 18)
-
- // Create the master window
- window = giu.NewMasterWindow("Picocrypt", 442, 532, giu.MasterWindowFlagsNotResizable)
- dialog.Init()
-
- // Set window icon
- reader := bytes.NewReader(icon)
- decoded, _ := png.Decode(reader)
- window.SetIcon([]image.Image{decoded})
-
- // Set callbacks
- window.SetDropCallback(onDrop)
- window.SetCloseCallback(func() bool {
- return !working
- })
-
- // Set universal DPI
- dpi = giu.Context.GetPlatform().GetContentScale()
-
- // Start a goroutine to check if a newer version is available
- go func() {
- v, err := http.Get("https://raw.githubusercontent.com/HACKERALERT/Picocrypt/main/internals/version.txt")
- if err == nil {
- body, err := io.ReadAll(v.Body)
- v.Body.Close()
- if err == nil {
- if string(body[:5]) != version {
- mainStatus = "A newer version is available."
- mainStatusColor = color.RGBA{0x00, 0xff, 0x00, 0xff}
- }
- }
- }
- }()
-
- // Start the UI
- window.Run(draw)
-}
diff --git a/src/unstable/README.md b/src/unstable/README.md
deleted file mode 100644
index f671cbb..0000000
--- a/src/unstable/README.md
+++ /dev/null
@@ -1 +0,0 @@
-This is the latest and in-development version of Picocrypt. It might be unstable (and might not even work), as it will have new fixes and features, so I recommend you not use any file in here for production.
From e3fbf25adbc06c70d3eb7309e30320edcd2bd783 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 11:31:32 -0500
Subject: [PATCH 23/32] Update codeql-analysis.yml
---
.github/workflows/codeql-analysis.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 6afcd21..8f0987f 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,7 +1,6 @@
name: "CodeQL"
on:
push:
- branches: [ main ]
paths:
- "src/Picocrypt.go"
- "src/go.mod"
From 8f183b6f95d6f1afebba633b0c3f68ea734de647 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 11:33:48 -0500
Subject: [PATCH 24/32] Bump to v1.22
---
src/snap/gui/picocrypt.desktop | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/snap/gui/picocrypt.desktop b/src/snap/gui/picocrypt.desktop
index ca214a0..a1aec18 100644
--- a/src/snap/gui/picocrypt.desktop
+++ b/src/snap/gui/picocrypt.desktop
@@ -1,5 +1,5 @@
[Desktop Entry]
-Version=1.21
+Version=1.22
Encoding=UTF-8
Type=Application
Terminal=false
From 82c21b298611c626a1edfdbbd8226297467155d4 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 11:56:33 -0500
Subject: [PATCH 25/32] Finalize v1.22
---
src/Picocrypt.go | 101 +++++++++++++++++++----------------------------
1 file changed, 41 insertions(+), 60 deletions(-)
diff --git a/src/Picocrypt.go b/src/Picocrypt.go
index 8a88ffb..c47e770 100644
--- a/src/Picocrypt.go
+++ b/src/Picocrypt.go
@@ -1,8 +1,8 @@
package main
/*
-
-Picocrypt v1.21
+
+Picocrypt v1.22
Copyright (c) Evan Su (https://evansu.cc)
Released under a GNU GPL v3 License
https://github.com/HACKERALERT/Picocrypt
@@ -45,7 +45,7 @@ import (
"crypto/sha256"
"crypto/subtle"
- "github.com/HACKERALERT/serpent"
+ "github.com/HACKERALERT/serpent" //v0.0.0-20210716182301-293b29869c66
"golang.org/x/crypto/argon2"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/blake2s"
@@ -54,16 +54,16 @@ import (
"golang.org/x/crypto/sha3"
// UI
- "github.com/HACKERALERT/giu"
+ "github.com/HACKERALERT/giu" //v0.5.7-0.20211216020632-65de69857557
// Reed-Solomon
- "github.com/HACKERALERT/infectious"
+ "github.com/HACKERALERT/infectious" //v0.0.0-20211215232025-9d20874ad4b1
// Helpers
- "github.com/HACKERALERT/clipboard"
- "github.com/HACKERALERT/dialog"
- "github.com/HACKERALERT/jibber_jabber"
- "github.com/HACKERALERT/zxcvbn-go"
+ "github.com/HACKERALERT/clipboard" //v0.1.5-0.20211215214929-7345ba96aeca
+ "github.com/HACKERALERT/dialog" //v0.0.0-20211215220206-17f428aa513e
+ "github.com/HACKERALERT/jibber_jabber" //v0.0.0-20211215234401-c23e432f628b
+ "github.com/HACKERALERT/zxcvbn-go" //v0.0.0-20210927200100-f131a4666ad5
)
//go:embed icon.png
@@ -92,7 +92,7 @@ var languages = []string{
var languageSelected int32
// Generic variables
-var version = "v1.21"
+var version = "v1.22"
var window *giu.MasterWindow
var windowOptimized bool
var dpi float32
@@ -139,7 +139,6 @@ var metadataPrompt = "Metadata:"
var metadataDisabled bool
// Advanced options
-var fast bool
var paranoid bool
var reedsolo bool
var deleteWhenDone bool
@@ -174,7 +173,6 @@ var showConfirmation bool
// Reed-Solomon encoders
var rs1, _ = infectious.NewFEC(1, 3) // 1 data shard, 3 total -> 2 parity shards
var rs5, _ = infectious.NewFEC(5, 15)
-var rs6, _ = infectious.NewFEC(6, 18)
var rs16, _ = infectious.NewFEC(16, 48)
var rs24, _ = infectious.NewFEC(24, 72)
var rs32, _ = infectious.NewFEC(32, 96)
@@ -182,6 +180,7 @@ var rs64, _ = infectious.NewFEC(64, 192)
var rs128, _ = infectious.NewFEC(128, 136)
// File checksum generator variables
+var csProgress float32
var csMd5 string
var csSha1 string
var csSha256 string
@@ -195,7 +194,6 @@ var sha256Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
var sha3Color = color.RGBA{0x00, 0x00, 0x00, 0x00}
var blake2bColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
var blake2sColor = color.RGBA{0x00, 0x00, 0x00, 0x00}
-var csProgress float32 = 0
var md5Selected = true
var sha1Selected = true
var sha256Selected = true
@@ -542,21 +540,19 @@ func draw() {
giu.Label(s("Advanced:")),
giu.Custom(func() {
if mode != "decrypt" {
- giu.Row(
- giu.Checkbox(s("Use fast mode"), &fast),
- giu.Dummy(-221, 0),
- giu.Checkbox(s("Encode with Reed-Solomon"), &reedsolo),
- ).Build()
giu.Row(
giu.Checkbox(s("Use paranoid mode"), ¶noid),
giu.Dummy(-221, 0),
- giu.Checkbox(s("Delete files when complete"), &deleteWhenDone),
+ giu.Checkbox(s("Encode with Reed-Solomon"), &reedsolo),
).Build()
giu.Row(
giu.Style().SetDisabled(!(len(allFiles) > 1 || len(onlyFolders) > 0)).To(
giu.Checkbox(s("Compress files"), &compress),
),
giu.Dummy(-221, 0),
+ giu.Checkbox(s("Delete files when complete"), &deleteWhenDone),
+ ).Build()
+ giu.Row(
giu.Checkbox(s("Split every"), &split),
giu.InputText(&splitSize).Size(55/dpi).Flags(giu.InputTextFlagsCharsHexadecimal).OnChange(func() {
split = splitSize != ""
@@ -865,19 +861,18 @@ func draw() {
giu.Label(fmt.Sprintf(s("Picocrypt %s, created by Evan Su (https://evansu.cc/)."), version)),
giu.Label(s("Released under a GNU GPL v3 License.")),
giu.Label(s("A warm thank you to all the people listed below.")),
- giu.Label(s("Patrons:")),
- giu.Label(" - Frederick Doe"),
- giu.Label(s("Donators:")),
+ giu.Label(s("Donors:")),
giu.Label(" - jp26"),
+ giu.Label(" - Tybbs"),
giu.Label(" - W.Graham"),
giu.Label(" - N. Chin"),
giu.Label(" - Manjot"),
giu.Label(" - Phil P."),
giu.Label(" - E. Zahard"),
giu.Label(s("Translators:")),
- giu.Label("umitseyhan75, digitalblossom, zeeaall, francirc, kurpau"),
- giu.Label(s("Other:")),
- giu.Label("Fuderal, u/greenreddits, u/Tall_Escape, u/NSABackdoors"),
+ giu.Label("umitseyhan75, digitalblossom, zeeaall, kurpau, francirc, yn, Etim-Orb").Wrapped(true),
+ giu.Label(s("Others:")),
+ giu.Label("Fuderal, u/greenreddits, u/Tall_Escape, u/NSABackdoors, @samuel-lucas6").Wrapped(true),
),
).Build()
}),
@@ -1047,23 +1042,23 @@ func onDrop(names []string) {
metadata = s("Metadata is corrupted.")
}
- flags := make([]byte, 18)
+ flags := make([]byte, 15)
fin.Read(flags)
fin.Close()
- flags, err = rsDecode(rs6, flags)
+ flags, err = rsDecode(rs5, flags)
if err != nil {
mainStatus = "Input file is corrupt and cannot be decrypted."
mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
return
}
- if flags[2] == 1 {
+ if flags[1] == 1 {
keyfile = true
keyfilePrompt = s("Keyfiles required.")
} else {
keyfilePrompt = s("Not applicable.")
}
- if flags[5] == 1 {
+ if flags[2] == 1 {
keyfileOrderMatters = true
}
} else {
@@ -1245,7 +1240,7 @@ func work() {
stat, _ := os.Stat(inputFile)
total := stat.Size()
if mode == "decrypt" {
- total -= 789
+ total -= 786
}
// XChaCha20's max message size is 256 GiB
@@ -1299,14 +1294,14 @@ func work() {
fout.Write(rsEncode(rs1, []byte{i}))
}
- flags := make([]byte, 6)
- if fast {
+ flags := make([]byte, 5)
+ if paranoid {
flags[0] = 1
}
- if paranoid {
+ if len(keyfiles) > 0 {
flags[1] = 1
}
- if len(keyfiles) > 0 {
+ if keyfileOrderMatters {
flags[2] = 1
}
if reedsolo {
@@ -1315,10 +1310,7 @@ func work() {
if total%1048576 >= 1048448 {
flags[4] = 1
}
- if keyfileOrderMatters {
- flags[5] = 1
- }
- flags = rsEncode(rs6, flags)
+ flags = rsEncode(rs5, flags)
fout.Write(flags)
// Fill salts and nonce with Go's CSPRNG
@@ -1377,11 +1369,10 @@ func work() {
fin.Read(make([]byte, metadataLength*3))
- flags := make([]byte, 18)
+ flags := make([]byte, 15)
fin.Read(flags)
- flags, err3 = rsDecode(rs6, flags)
- fast = flags[0] == 1
- paranoid = flags[1] == 1
+ flags, err3 = rsDecode(rs5, flags)
+ paranoid = flags[0] == 1
reedsolo = flags[3] == 1
padded = flags[4] == 1
@@ -1434,16 +1425,7 @@ func work() {
// Derive encryption/decryption keys and subkeys
var key []byte
- if fast {
- key = argon2.IDKey(
- []byte(password),
- salt,
- 4,
- 131072,
- 4,
- 32,
- )
- } else if paranoid {
+ if paranoid {
key = argon2.IDKey(
[]byte(password),
salt,
@@ -1527,9 +1509,9 @@ func work() {
keyfileCorrect := true
var tmp bool
- keyCorrect = !(subtle.ConstantTimeCompare(keyHash, _keyHash) == 0)
+ keyCorrect = subtle.ConstantTimeCompare(keyHash, _keyHash) != 0
if keyfile {
- keyfileCorrect = !(subtle.ConstantTimeCompare(keyfileHash, _keyfileHash) == 0)
+ keyfileCorrect = subtle.ConstantTimeCompare(keyfileHash, _keyfileHash) != 0
tmp = !keyCorrect || !keyfileCorrect
} else {
tmp = !keyCorrect
@@ -1586,12 +1568,12 @@ func work() {
subkey := make([]byte, 32)
hkdf := hkdf.New(sha3.New256, key, hkdfSalt, nil)
hkdf.Read(subkey)
- if fast {
- // Keyed BLAKE2b
- mac, _ = blake2b.New512(subkey)
- } else {
+ if paranoid {
// HMAC-SHA3
mac = hmac.New(sha3.New512, subkey)
+ } else {
+ // Keyed BLAKE2b
+ mac, _ = blake2b.New512(subkey)
}
// Generate another subkey and cipher (not used unless paranoid mode is checked)
@@ -1757,7 +1739,7 @@ func work() {
if mode == "encrypt" {
// Seek back to header and write important data
- fout.Seek(int64(312+len(metadata)*3), 0)
+ fout.Seek(int64(309+len(metadata)*3), 0)
fout.Write(rsEncode(rs64, keyHash))
fout.Write(rsEncode(rs32, keyfileHash))
fout.Write(rsEncode(rs64, mac.Sum(nil)))
@@ -2032,7 +2014,6 @@ func resetUI() {
split = false
splitSize = ""
splitSelected = 1
- fast = false
deleteWhenDone = false
paranoid = false
compress = false
From 64a032e63fd5e828d20fb4cb741422e5809cb1a1 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 12:03:38 -0500
Subject: [PATCH 26/32] Check file version
---
src/Picocrypt.go | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/Picocrypt.go b/src/Picocrypt.go
index c47e770..2ab7563 100644
--- a/src/Picocrypt.go
+++ b/src/Picocrypt.go
@@ -1020,6 +1020,14 @@ func onDrop(names []string) {
fin.Close()
return
}
+ if string(tmp) == "v1.17" || string(tmp) == "v1.18" || string(tmp) == "v1.19" ||
+ string(tmp) == "v1.20" || string(tmp) == "v1.21" {
+ resetUI()
+ mainStatus = "Please use Picocrypt v1.21 to decrypt this file."
+ mainStatusColor = color.RGBA{0xff, 0x00, 0x00, 0xff}
+ fin.Close()
+ return
+ }
tmp = make([]byte, 15)
fin.Read(tmp)
tmp, err = rsDecode(rs5, tmp)
From 22da08c01b22b924a6f5b67b8ebb1c468d33c000 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 12:04:08 -0500
Subject: [PATCH 27/32] Release v1.22
---
Changelog.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Changelog.md b/Changelog.md
index 6416465..a218d13 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -13,7 +13,7 @@
Improve Internals documentation (header format, etc.)
-# v1.22 (ETA 1 week)
+# v1.22 (Released 12/22/2021)
- ✓
Fix keyfile order bug (redundant, so there actually was no bug)
- ✓ Remove fast mode, as a change for the normal mode will make fast mode obselete
From 8f3dd5899f56cee94a35b334bc12deeaf68e8ae8 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 12:13:06 -0500
Subject: [PATCH 28/32] Bump to v1.22
---
internals/version.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/internals/version.txt b/internals/version.txt
index 64153b7..7999cbb 100644
--- a/internals/version.txt
+++ b/internals/version.txt
@@ -1 +1 @@
-v1.21
+v1.22
From 29851d0d7e0e6b9ff958339aa550a3c0c4e750b7 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 12:14:01 -0500
Subject: [PATCH 29/32] Update Windows link
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 71131e3..c5d2b73 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Please donate to Picocrypt on Ope
Important: There's an outdated and useless piece of abandonware called PicoCrypt on the Internet, which was last updated in 2005. PicoCrypt is not related in any way to Picocrypt (this project). Make sure you only download Picocrypt from this repository to ensure that you get the authentic and backdoor-free Picocrypt.
## Windows
-Picocrypt for Windows is as simple as it gets. To download the latest, standalone, and portable executable for Windows, click here. If Windows 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 here. If Windows 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.
## macOS
Picocrypt for macOS is very simple as well. Download Picocrypt here, extract the zip file, and run Picocrypt which is inside. If you can't open Picocrypt because it's not from a verified developer, right click on Picocrypt and hit "Open". If you still get the warning, right click on Picocrypt and hit "Open" again and you should be able to start Picocrypt.
From 73dd38b33e4a88bd2744e38c0860dd23e853b987 Mon Sep 17 00:00:00 2001
From: Evan Su <48808396+HACKERALERT@users.noreply.github.com>
Date: Wed, 22 Dec 2021 12:21:27 -0500
Subject: [PATCH 30/32] Update screenshot for v1.22
---
images/screenshot.png | Bin 70970 -> 41482 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/images/screenshot.png b/images/screenshot.png
index 2e56f2c88ffdb27d9af64fafa1355baaee9395d8..66e72a653b0d686cb52b993577a25eca3d2104d4 100644
GIT binary patch
literal 41482
zcma%iV{m0rv}QWC?T*v2I<{@wNyoO;F*@nkPCB;Dj&0jE=Jb0rHSblushS_DTkGDO
zUH6>3_gd@23RjR5M}WnJ1pxs;kdzQn0s;Bb4gvy-0SyYgLr%TU0=#^2QW6&eshq$+
z1_E$%L0Lf%keXPyH$w;@hOw8>bOHfE?EClkWyr3?1Oy~ETT(<a-__6G{hN{B`Iu
zW`lc4kb0&Z9q(boiyhZ1oUWt>qNJmV8hH&6s@<(~DsV104cIL9yU*1M%XsEq&jS
z&>rH8V%DWOKO+lw8#g!iZTjKz`sy}=W^J%IGPqz#s0ORw$Fn)T7!b^j%@j&Q=)@62
z2P%9)l?^0@W+R>^0|K!B8U(HUG|P@`w=H((jlJIJ7%c>@WJQ_7l>L-$?{(aG-e_le
z_i$r&2Sai^1{Xw@{c53K{%*B|p@nH8}ipatdsgklP^^4sRAAJb%Pl^e;5c^bIaK3E)bCmd>syZS*b9Jnj;PzjpZn3eJro`VK
z{k|p)JK<*rP~CoQb6yx^jASP&m#0|0F>vZ*G2>I}k+P
zmGP_bn&99FbmLBfLY1_``C>aSZbr%I3)e1(>g7xZtLC1nae9D0Z*&n`k8#nrk}?M7SEr`c4|h0ilbp`
z*ZdquuR!?Dyo)2OFQA0o-%A+N*Ar+rsQFZtUoXvqRxvIl}}229}=
z?$nwvH~mT#PBH5{-i@BZ?YI|w)7OKFNBQkN^G$gT}^Mga;V&No3zUUt!RN3
zpp1Qa6wNZSNTurZ_p>b+4Wca;T0AJ9K6~`x5>GwCZ%v0x*EHguY3;&&1X|P%Zww{A95a-pONl7+Yre7fD84O{pKU@3o?!`6
zLSBCm4i}b0_vO@NE(l&WQ`y@)9;$k-8u{rNxu}gl@Muh~zP~@Vc`&DGJ`YP86*?Bt
z02?!^Oy2iIgdHQi$n5u%U1-JACF}^ED4cf0=K>01Bm;qi`3Co6uvxd(b%l(ijyK*f
zoMP2FUL#;@$MnQ$LM#qBrttE`!NtlCmp0>K1Jj&u?|p?I{6_lCgRF1mIpBirA05g}(~fu^=+A4sM(UDv)Hpgj4+AAmAw}zoPB&7#G(O2Drm-Ik~wb
zXyL)8rsSd_Ix{peFjwslo~(mw}iftEyay@Cc(R>-vE4eI`9>isnV^^aj3VSpTHZCoWMVNwN6G
zr8*Z`8q$yzFnyYv0zw%$+anncB^gNkBU++eqroB?@gA3oqFZxI$6hQ!S^4f(Lev}%
zPx;m*URu*$iGz(^2s!wr+dL?GL{yK#Uaz;7nO=z?LIh2Z6U+15Jz74R7}q}`Psn{=
zXYU^Lu<(h#Lg?|$g_Q-T>bev~r0>epeP314Q!j!Le7U}QU#N0P
zm8-=eUQA|DjzBglx>;g;Z|^|2-8&@m%3VH>v@A(vUw%@Ob$@Dlnwo~Dyrg7n=LZQg
zbbWojl$2Dnd%lRid{uSH&q!nugt;(_lanKKnp1+bv^1V92`bvdoo`pqltiw_*u$b_
zq)*yh@jydH#xYn08GdSeQ5b9<7f@;q3;z2vUg!Vd
zC}#OrIcU(2I5wm
zI^IP}*qxPjVUWD;|cCIGBMHOe6M8y)rA9>oG7^wcJp}e
zYc=tw)Cu!!qkY@9|J~K*{O;a4IyySJ^V2~^=B$d}=Wu3&nu(f|iSzlh^W)>!;|8C{
z!P)UGR;~iS_p@K4jh4BoskWw;@AZ4^p)kF%U00PRh6DfT|Pfs=WOrpCq(tJTK4J7;Jq
zHMNC1PN#^-NQauw%U|-Lh=lOs`KGrAv*webt+MINrYBK2ae0?rAGp!SeV-p#p;@uT
zlsEO3^FJ;F!1yb4T0R!_?5H#_A&E;d`hVYh^1ebeOM$vT_$7qg{vH~wc8
zZ$?viA|Cte_GO)d1PrT%bAIRG$l;u)tRheTD4eA#cJzo42Axh=hyX^;dMobjanAky
zear*1wvVN1)5D9s#JMW|clqJ@(=NO931*g;hciCsH}pHFBhef`D9Tq?6h)*sKF_DA
zd`fvzkn>3It1Td}h%{>-%6-0Xt%tK5-ewlIaZf-)Oae|C?@nZ=&wpMFMX{97QK68ba-`6-<%s*;;-=9jAHGMgai1^;l%Bwa~
zcsxeacMt_wQB<(PkbcwY)m^(;P&pzV&7O9U+tpO;`3D>yUwpS5#BZVebxwdk*nVB4
zpnuVlO42{s@CLU1$o3?~fvAdC%}U-xUbB~BVT-}QsjQ6~d~
zLjFnDiA6d8)nJd5raJD`{5D{g=#F(5Jp*!cx^Y>zR7z1BIQE2;V7VWCxzp7Wx-!+Ok
zI>LGm-Ho7aX*o!u((8BiWMyGtC5wph`d5w2B;5=(9`amkaU8(ki#WX#X&X${l7D3i
zCm-FQEh(YGx`L?CYNW#Y-N$sOL?bEtx`@u@d}d-_9Aj2QvEJr7nA!&_PBQj1GfKqR
zfjqW0d
ziy*W4L)w81mCd*ZhvKSu6n!ISXZS4EU%12Jf<=7#^tmHUegU*3t%li>PYP1C^3
z%-o#8^Dwg}Uku{q)u|LUm&f;pY<6fV3<>J~P>-h0Zc(m@kWhfZ>$HNu(R!N%b-{M&
z>SCslmggPeTGsDvArMNV4U$;GuXZVku3g#aCy
zcmw`GSdS3i#?x6&1>%BYO^V;{LQX4ot5Rweh9~_tnO>Xfwom0N-&cpziLPqmZt~Jt
z&%73&*%hFJs55=p^ScaG-H*KV!{zlJ-sa_Qw0W{Q4Q10gx$V|EfO|Tm*Gu4o45+v`
zKfXR(nfGdXXR=yJl;?Ye4W}-(Z0ft*nc95Y^pla5)sU+;yj3G5&F9tj-K_msbfNKk
zd0QniIe);~zL_u2_PrJzPGzyzl_%u$eq5*kI^T3{b%Hu9TK^`m^WG5B*T>Bk2jjhJ
zLkXP@SGw$>Hm_R>)T%80%hU65=AgT~J2$?!t3?bNPv=wD!R?1py`m5g2VbP=SXDh<)=IZ$8f!uZLWbLE>qpQ(Qr_KCt5mDEh
z%teez#k2A2gx2P`$=u~6m0@Ul3bwJeA$!pN)ZAR~hR>UyK3~M{MRG2xxLjIMMPXra
zRr2kdm+I9+Lp^IJE(H~#!!JUBS^uS_*XVaK)DxVUMlX%xi;
z)vrnLPmAN}Y(dmov(b3iwhL-%paCGZd~K4W{+V#4N{`fPx&c}PEd}PoNJ7=@kr5Fl
z^2pu%{;8>oRq7^!m(`5yXwa-v
z=%!xcKKTFued$Qn*H8ghZr2(BfA@Nnk&ub-`u17gKU2%hIRO3)0ilY4W^mY`AcHY7
zHs+0wRUBaiS|a>bXYLpncr^R;l!lIGuoa?1K~DL%A$;u5pX9<6kOm-Q2-@0KuEBr~
zTc{3zs>l=`s&?gRq7hcoTmAm5
zHu=a)U+|=|e1O1=N|1EFrGK^ljDnLdYv8
zC27eM5nk8bvK7@&6dLz=)ax{?omTb8{vQm3=t&Z@v~s+c96$w1c%=R#ANpxv|10SK
z4+ek%D&R@^GYuD1w)5vAHfAFDzT(R=nJ2Oca=$dl3i$Zm?X%WOD}Y*e+*0)DN#Yf#
zXldv$!$mMK_UAv2LJ7f9z^`YvZXpz8c)>_z=_-<|Qv4^yB^3x0VNG{Y%Yy)(<7aZf
zPbj=!aO@QQg-TC@y#KxfLqLRM?h=iica91b9#$#hKQdDfHM)nUDxy{?{yzu`L1Prz
zYZDGNHC$$-*TUO9Rx;24fyd|4%Yu~j^txrA^1`B-&cwPBh?37Tr-v@|esar1J-lgm
z_ZB*la0|Ii*5lWzuEAQ9?cR%?p0^jKVsS*ge+L~nfwHNr$LW*&=9iM0eBb!N?7_B<
zFFK8H&FGcFOT$NMf}m>N8h>CUtAr?vmt_mI6y0fEb;a`iSZt)jDz)nTc)P917k5Iz
z+3Hy?JqhINuMh9We}50jXR$U;
zqCTE1v(VAe+??|qi1_(Y*B#`SAnC)cq{+ifQ;l8=O
zLH*SXqYERXOA(F7;&>DX^xe|xmA&(~6;SVrSo;^xBP`~K1e`a2)7XqDy{g3@%|VocGrFdwkgZ|$-zng#+ZyO3o~;)IrsDXB)?`W
zK0q}%)-4!L+MJ&mKssI@1|-vEjVcZG%$M
zj=Z}gXky&ktL+})Fyw&=6j6|{LC(=k;!1RMeR`BF&yfj2_(k1TKNGXX#IJZ1dPpx1
zCx%AWH|sYTSPc51oGv$G%gW}H4yPvFg;iyA7Rv`_W;2)}AXc2AkdQMl!B$}j`rx9W
zA@>A4PntrYScliHHp`A@{o$KU*3WNUpQ3^#=rV^J3WqPMrMmUI@+V)5vJ{mCo*twF+-iPEf`CTjf}q%
z0nKy}9FNf&$4FMc!Fl^~9YBBO6Y^-hO>m1240KiLTG`lSW@jToWHh;7CzY$mpU*7U
zWs=u%wzf7~>vi$5GgMVqBPVlG5ra^CoB5jm>Utg_FVS6@knqcVB2$|JJul-Qfc1P7
zBk%=CU~G_&*J<(CogI5cQIhEt$zLXvSiJ|?zR&xaSN7;2uRSoZAwm}T6Nrb+&eM6C
zv&F?lg-MBt?Z8FbZkI;tyF*bu``zv!J?{@gM5eOtXJm}TAVGp!o-e(H()`a`U>Til
zk#J{H+}f*s;;}}Q!;L<-2lHL;uE(QE44;>utx!<>(Xn>Zf0mcwu=w70;2MeSfke?m
zDsH5%Fd11aN5JQL_SyM{daUpBfOA7hL4k&d#DMjmp1iG@dDi#SYUl8i?V%=^SnTler&R8K%USTQ+g|rM`W@z~yq)Z7GFG0|G6<^f&1JW~&cCGNl@KbJ6Fi7EcX*E&uSKQO>Xp)0`)>sxlQMkewAFtQW$&>5Z
z&$LHG=%a-Onu1iDiKnMo`RRl>kDI$=fM1oUv029*5K|R?e|dcBDS7x(a#gC_
zc7IM&p0WiXdVr@91X*BDY!Yv>h0cdCMvJOSpv5P6d+U69vC6=O#YDm3v>n4Hi@{~e
z6OF{QV9BSDIpViYx$8XNgF%bCIN1$axw?Dw`#f(VAi&>xboX>Op!*ZL!RsYt@=jKP
zB0RkQ6MDq8-wO1$qwHG|ZHJe$-wmtp`(--2?FOpvpwdFC#Cw0Y5I^P8l#`+Wm++i`&X+B0*Dr
z#nt^)udF`e4@0VZVPawmsEbQU46XwilFJHS9=Pe239T9Fof@C~eC$%A)2=N6MI_|Y
zp=ZL|1}CGWtP|195y$&{3R2~E+;Ob#5&JU5ms=2EW@_Gd@f&SCF)`8YH*G-D(12;T
zW$&!G0RRpdEx(#CVX8T4T2eb_>>GBQMK$|nhg9jbEoeCw?TSUe*t~mwA0e+~`=MH!
z#cn0q(77O&)x)CI;r6@N^;
zpz#kIlPH3$tHd^y-wh$I05YVtXUPN(9`??>pl>i3%!`!F!8m)(WGRK=&4$XG{
zUf)q;#aqy1*wQP3)y`h+uogBU;`}O-9|Ey|-x=UtZCd5=1~WpDSRRl7;x*i
zIqa?tL=t%WR8&+D5D?f&+BPCrAy^(C>y(%o1V-U-?u?fzSHqvJveVFL3k!q&;I?|f
zgEO1N<@Yr4&(3!4&CVma9^z;ohReya=zYiM^nT)>?+`<~-x35QcY4tDa*x{-DP4={
zc1}2uC1S^gFrzF!uiyF#AWe#`)p&_VJo?_Z(dJ=@xrT;aZ*MGr$NaUSirb8vZ2N{{
zbYCvobbcTJ6@x?OF>2?hZ<*+-)xdC<|xXz%d2eSD%nem8HKe8G49rNQR-T6FsrJWMem8fPf`Wnq
zVTiQ4)J=VTaIkRYe8?*B-Wsb?PB&B2`LjhbhVn3MY;47HY0f)$mW>f#+?J-gK`=?M
z)Dh~30V*FrvZHDos7OTI5BKKT1iT)oic!dns1DQenY@3Qsmwn4KgTk@NAqf6@kf=l
zuh)32Hh%g(eT6%XYAZM{^Y26$vfsQ|
z|7t#g&uhOm5smldV~4BN=X_aQB
z;&UAGf$;fy*XP7~Tk=Gf=-(F4ZGhH%je4eP7Go6JE~sO!?%7f*6b!&Tx;Pl@?Jq~H
ze5(0Xj5(hD+OsNsIG%C+F*DH7%5M&t$>p^%IC_#fVAn(B_gWF+;~8PMrr+Z0bJJWS
zk;-oc$>pBeyjMH-F8!OB{%o0R6=5+ijvkSc`4W#8z3YnCZelJ3tTu38yXn+8|oybi|@cTRdKUQnQ!EkML@Xw^A-sD
zz!CoisiC^0+7&?%uy)`AXKEWoF)hhXt&79)Hvzs$3gJ~?JpK7dLr!xuHVOGEPW5X0
z+gn;I)J?fB$C*gQX2;ejH6~jItye3ZxTU2fz^4QTeg%8+u26;X8dUhIO&Nh;!^*5e
zilrFZ+?(A6?gA~>Qj{k}^rsZm>qd;#mspCc)e}3qrCDf70fT3-6gj-?!ANlLV$AP%P3TJ&^(dH^T{dK&V;8`-CS%q0n-(gv(r;#Yem2{y~Ly3pC)M2%4m!
zv5B$`XjXw8-}J3{`WyJcs#UTLrDp-JT!l+@5OUTm_%?w4i6Q^=peZo)VP==
za{pgJV&tF2jA-G-om&Zd-;Am<7}Rdlvbd(K69KD-OpfgSe(w!zF0|4-^gkw~RFbLi
ze=lO~~^z>9!?o#uHqoboS-`nx5^)~LMRqd2?s~;8y
ztAg*ZP1egd4m$YB;Uyx4;q(76YD(hW)ii1F;dmCW^Az!KIb;!I5MC91{p^r+9i72w
z!f~@Obrf!GZIswYK~QuH~EDM1~Vn~`J9e;5V6+oQmYX);bNvNJLo
zPA2Dm+wg)<&)GZMW3&921E8;h7H11!I3JUv4p=%oez)FX{XQ?cCy@%5IbGpxzeSz$WXs}9^aVGUDl8M
zGMwqdI$A82H#0Mn`calzdYt!s%gYN0o1XVa*L%mafxW!|80qn;wwi@pc64;)f`DiM
zB>L2Mu-8ZNopJ4^YHe|^SCqcckkAmrHSL=P?eolxR+n?!t;^Vkv)NlG^NG8Mo0GLt
zv`Xc9IwTUjg@1X5YUnQ_8-49A+0f
z4S9KZ%B3?G>aI5Wcs_{SS$e;;%||
zDOpPKA)wiKN9Xmq`-$S?_2}wokIiFmu=oDDgssiMOZsM^X|}
z)okHUjF6T?pyYr1JGuA>^!{-ztJ~Yjl{zi1yO8u^u@~#Pwgg~c(o#|%OSKRyVX7@2
z9&ti*_hs|t!giaT?#HOX4efcofBQ(!9&F$hno$9U1dy<>kGLQqOij#;)a}NzKLM7>
zoIyvxS0fp~{GAw>p~Aa?|1dQmb}Puhr`(!N>OG%UAxoiyU`?)!MuZ=XPfY>az@Y^u
z&(fa|7R>Y}7=}iz>i2hF;Le1@K5ka+j)4g@q8$v%h%UgfaRC`+L;_
z%)`ThOkUy^rLH3-1WI3A6R#MFNjDnO3-nT4O?I))I@l$YV$#EvcZA<7o05_+;C8i0
zz~D&n@ZKAB#k2f6;o
zIIhNrL{D>ZZf-zg!JY|KQF`RxpcHJ4*J`ot#dK^uQr(MEB1i!pGKyw^znP)l*~&FH
zF{x%8{yLjrvGUgHdQRl`VQFr4dVDdVw1wX2<{T_Mh-qxR8`vxK6nT7njQH?yq!!oC
zy}eCa2WrsJ&1=q`%Iid7EeUvnxZN%qACda;^V3@?3$<8KP*V2Qg|*r#!38
z=;-K_)Rh-R~
zqQQZ9ddjhC8pw19dU%bX249Mc_RO#{GaHo`2%N!3N98ylGU)yJ5u>s%BmwO$Lazkz
zv%4-Byg3|tcJ|gU-UpeCoPpmDfO~u!8;lc%j<|R@fFhNzpUwAcFxtt<<$SY=p%N3`
zT_X|5Va`rYr=&PBu4gL{ydmlS0T+VKtX_|3p0Ef&=Al*F{w5|OHerY~8Wd=dQTh4q
z=4$DXlip-2i8w*SP}M!3#$<#09sX$QD<}ED;9%(bazXK-h_Y~F7$DO0g;~wl?pIVZ
zHrv|CWJSrXP%~C5D43jg@x
zWTno_O<0)$O9&FibKs@yq*5&jqLN3A*R?&}9eKSC7V+_x8bRD8Yto4Fod+`lLHG
z(y6H_MmDB^D5*`at=ngb9*E(|NptElkZc0Nu$i_wk=tu?)99F*yiH@7=h|U0RW~H
zcrE*LX5D~}wzWc~DnneH;*b7|*kt4vv8oKf;sYs)M!l-GB*MVy&Pvk_t%zKyAOa>$
zWwE>i&^@kUnS94mkTyIH0gY3&0zrrl@QCI~+Vhd^wPpKqxov}DQT~7fBotPd1TuyP
z2b3L%|G^Lb-^}4)Y|oCz@mu%d(lzY
zBAI_;7c|a^9;@Nn4c<9+9%%G}72p8hQoZa#@CR(Z3Qk;}QbixoH`M^Mii+3$HO9S{
zqW0ydVV>VOoAUTP;uBt9UtF*5feKq)(@0NG|IK1)a$%u$kD)NjwDi|PuR(H7$1hUt
zI1=tpn%!~gU$e6;j*dC_LHZF0}U>3mC*=S&NPY5kv|yA?H#kwA1I|xB{KsHAMw@1g`S{GtzJo05#f8d>Ogd?65L~~^MLkx4*q{FC
z_SQhj_5H#1X}#;=d~$U3V8M_qevBrsqHth
z-qzZ>zxQ5H_I?ca+eg}{40%QF*AlMR{Fs=SmX_2zbmA|z^zkfJIh`{MXyj>E|j_&v~0$YFR4>XHBQhrY>qbs*9f2qYfFW3p_k@3km<`&!#Y_
zvSxAn>hwsbSlb>FLWYlJmIAtWxSb}ae^JH|toJMqtgD70IWfFXaXm
zw`n)gXJ!xb8>8XT>T(d-l$$phs2@I1nM?inVWzgQ8i^UY(Mg9MacI;l`8Afc%pZ%|
zBUz4MI}u&FGbpl`)boB&_AVI4SPaVTBA6?fATcR3^LJKE43qhwvpSZ!S6gF`F4qvh
z^btDxQmd*or8pFke5`ovZbU@n>-|N@z{W(S-{r=UuE*#>ZkgzaRoS8B?qK7OX>WtB
zPO8a@>eVBFmw*=w;pSR;yqG4C1s&*1VRh+!0u)gC^VPoDV4mEGp(yN7y{(yzkxrY{
zCGptm?O>*S(6_fKP2lAk#w#L)BH+3|pRg6kXY(9S)ffZ19MJ1h&_kM_Jd_r;v@|xV87ZMch9UB8
zFvi;}e$9D*z2ow^Xg|}DQ}u`+1Q!fO)N3cpdQwL$waAk%DlEL@cBeX%3itoN#O-AGABgN0Zj
zg^Yd3$VlTd>$g|KPDF?X_aJ5s`1sgH-F?8u_P)pfas?XkA6$I@T@<_oKinb>TM~ZK0&t+e2e1ifkBt9qsINCsEENWP
zk|9N4yE`-U<$MWkqSg1U&hT7rUxQ6c)Tf$@WwY)kcAAQ0vz}gmSLl?1*Bf;DPukk@
zgvW;I8f-4aRFp+jH7(w!=VFi8TQY*|MxDH6LJiRExjyk82bU^*0%Yi)OcN86(IoJ5
zd|knbThsc6x}d_o?|=!0nMrYjLLZ++SsBiWrLj7%v7v6jVswFmjJ%`YgIqs}%Wpaz
z{p$X^&2p>vJudc0<-`Q7qU$%*qO2=?Lxy3wF&bK|A6Ax>6qI;4IK8~V46f4FP@;WU
zp<0Cn1&+@rP^yD6&Gq(}8yQ>>f4e(<>S}A*SXu3L&1L#bblx83`l5^G%)cHunxv3LV1KR>TR8QJ%({Lva@0M=~
zZRvH|VM~vXAzxl<_l2=5zl5^YyB|Kt?F$*o$xU?plwilg68G|QkJssZ=^tE)fK!F)
zH(i}r@2cGF{;lhG8FD0#&HZvSe_Fm+vDt1TKAc3OvjAEAW5m#2u-=qNSWxWt8>Lmq
zZXYPfcV-@tC*~u5U2eDD90Ii0IBoSw0V?I$nXeasMfY^0foAPI$;vlq+vBcZ64>T_
z7O<2D$u6hwK0e;RkS-YlB+0?q4zktRGxeb=S+|^Us1Z
z(LP9JunyMiWf+mITutKW9T2+g-?(sH@vUVFjF0*hrixt3fkdr2@nZa+jQ8MC|3EqN
z>ye^@KR;F}(9yF{CVMIN{iD2bQ+`Oti2ce)*f|)D)~?WQ1R75n`ALK6iKGWy9e0l2
zF$m$z{4(yR7d4->MFVVHY!h>*`*5`$P$l9R3z#b^lu*Hb(5=z?Q=kZ<&fWIu6w(Dd
z>@Ltbs{{6&tgNiN5JDvOYXmBPL3Md?^{$&18|SmW7*$-2KD6vOz{oS$OP9{h&bZZ%
zMqOxX`Wq8dry$FN{G|N>DbN6E!q-U)N}tG&e=}=6a5tMg0|{jl>-OR5r`L
z=!=M1n4_&Gmqq=vlblAIKYwIoqb7A#?n4hE6Y&d0rQ^JuBhh-|!e?GQJ#xQbT0TH7aLV!1>jLQIJw9u$ahUDXz2iN)XvG-TA?zs<3@Cp
zinmt*Cb{~Q5Ku}aE?X$cigxJ|Y|79Y-oxZMUh&>i?;zkg}xM^abkC$d|*2OQnG
zO_zyDzgIN0AJ3P;Ja)2nJO^?w0gW6C0Um2&c=%|0_lI=|mNi}NmdKRhbuyiI=a!|n
zw~^6|e`g01Fb6|L2&v-mS>cTwEJa284S%W$sSKB1NU99?aa^y9%N!{X-6uGOVFxFb
z8pqIp+QPyjC!(d9d5bd7As@;98jF~LMp$K*9DpQBjIhTiCj#PAbS-2!fEc4t*Bn=C
z`h99qT)OO@cg&NHlZRFZWqEb68
zHU&jWZ{USY2G|mNQNo2+2RWMJ;#tc}V(xcg0}z15_A5L`OA!TyLI!H8x&|;&WDo23
zqcT4q!B5naU(mZq6*q_N0QuvQ}G7D^W(2)*Fnns02z)j*XQauLxK%c8(eHOUN-zBdpZh
z`f$I|W2sB6C+WoW=8?2){QMQ@LR%avk*F%;FP$G@)M+q+(S!_Hh7}Tj-jP#XEm8Rh
z%Arx~W-wr`RT%It-7(*Sk6r&mNyfi!c>J%jG?RYyUUbU@FnB2-jK&)m$;zz2h#niM
z5+GHX2y}}KDZHn9sdZ&UF(?0t=OUyL(Y4aN^)yRi`=0&^=v3p<`Z1|E;w*e#7gvvp
zt4`{|KO63Uo4@P>ExY|s6i3}+G>snq&EN&i-w
zwPnJ*^B#S|{P^FnURu4okoWS2kfo?%#IRosQh&GQ!|WkTm-FzT*QkQ
zR{owA1oYn)TJoR-m$21?Cl9SGPb0cC#GU4VK|x|vn2_FDrR&KrG~Y(8JX553>**D9
zCzAM~cWxn?2|Xdj(zLXkd7<;Rw{EA8r|ze|3|#Kdf!t5EiZpOR!6GQ4AdzBYQ9$SQ
zptuP@RQW0+RX`=q0Z-%)Rn@B|I540|;4A=f`R{!s^u>R87qs>nW`{hl9!v?T!YNU;
zl=Or5?r)emrtPWCZ^cCob3H
zx$D@9)ldXN?At5F024kcaj4hc-n`%fvf$UdGbXeCD_~-&qt$J8aS_0>;Nj3sZuUo;
z#p+&^LqG<%S*`EHLkV{V=)-@I=IOb)rP&!4zWtL}%N6qL@O}n+@$0jDljS_B*YL!I
z+~n}o?7WZh4mbmn8-ttMpI!=~+-hJR=*81>x~66c_LXR^$_`-Z0iuzDjH0GgsiNW!
z=0xWC$r>S~S-M(9&hbTMB~?LknD!4XKm@cM0{KJ{hKh__&)1fdlS?yE`#}j6ra8L_
zhz3PLRk1BCob|6jA=^oqM~N`cCz~ewDHg4j=kdI9PkB9AB!fM4P@5b<0s7I&*Y!u&
zZ&!`#M&KA2Nt>OSJzre`N`&|e0I1aM*3LGI-JE5t4Gq6K(~*)IU-tO*UIh33hQGRP
z!=lDi4)tkkRH$)D|A6koa@b#Sp`t()MN52ZzoAuImsnss4|
z$8HA$hvbKh#%;Rul%AW*wX#yTnehOq(B=%mI&B_s=|lWp2L_uL!`Eo$6Z*kMLSozy
z5VlXdlT+Mo7vj7rjIPe^BEb@5X?&(LE74uMA_~?3oX{vmpibM%LhH8@UHe5TpEZc&
z{&5X=)_mF|wEPx{oT=Sp$Gx&zrwRz=b(@-fj6iE!UyHseZ*66che42Eo*x7e4rJ4W
zcKq;gNuZlU7A*Y>Z)js?9KBmByOYJ&P*ZDEj;%*XX!Jfm*S`X|C3bdDIc^RP%y3dQ
zN2u8*3NOy~uEqZaiOkKYc;4UT!h3T0`Gsd?6m^|D!QqD|*Yv+lqyO(etv}ZWHGG3vdoPX_D_@
zp=WS;$+cp_J@^mwjz#_bU!=WtP*%~~_e+C>bazUJbc1wBcY`1e(%s#i(jh60bR*Is
zjdYhZ(w>FC=bZOE^FHs)nK}OF%q`rr-FvUKzVW$~i9PP_z%C_zg`S#I-Nv4P@Ds1Q
zfLKsyPI=hNQ!Yj_Fd!%=?XNjr@TZW(`Rv+QvDFyw{^4G7Fr0LJMqD23s~{mku6TVa
zY^~#?rKPr#R(g6em7t#b*EQsm)y9fnEIYZ>RJ~$@y#v%D*-WE6K3z_Ka=LO^yq$2&
zy0ho!e{yrM8cm7(ea#4QRnZQiL^>Hn7HBb3dQG-Jd`+m5#QOG7^h;hhh{=PXWL;f7
zum>fP@sAIFuQmb~EFmG^?KfGRk8ayVWaJygtwkCVyRV=o0#ZPrcMQdMdvmbZH#Q4v
z0$hmOvvbo_c=y&eg)D)gfuS-l2Tk|~R}czD4I}11KqvYo*c{bXAvdvZBz*~zAw{Tx
z)9i0S%|sG~^zd-=7ccGP*Z}qB^Ao5^g6;qiH9WmM@7>O9SL}T4i3H2M&B5_1M;`iqJhib_kB_36!x1ul!7TICk&UB_
zcsV
zX>~pVf0WMB^4d)Uc;W6nB!@=!r~B*O5{Iv$p@T%B;gF9tx2TR?B)r~yVJnpE;b`%`
zw%Us&4nrl@aS6R@m58u-eYmzZGjmm6-5IzI%&Gk!wWd;Wq63SRWXx5M*OB0#D2zWoKx{5ScTE!A0t!a#23p>7lIY^#NoC-g(SP
z9VpYYtJl5G79qO*yw`q2jTv>M7bk
z5-N@Z%Trb!LKTktrGzAs=FL1*OFWg0ty!_^fgZ??=c)HmC3D%d+#tvI&TVQ<`^M13
z2vK`sX#-I{XbK)Z49h_#WuQB>P7vKkN%t*km<>6kv&i0E1hW<#Re}5TmQZ-C!TAV_
zTW=qTt1mQJrvcIRir}D?kI#|6k|Ei4rYckSs4vvqdV@@F2MLq>iUt053Q
zLthi9si*`Bx!?4LoVB?@KRy|gn13)Yh!gg>cYgS)zuy8$%m)H2s4kd_>!!w^)+foE
zS1mTs#(+2MOmwB^st1SV`n|ytm)W2p1k1vTM94L|xbR~SMJP84rWJ^>fl^9fbj<;4
zQeu+vneX`+H^MioMaSK%gMA^^bb5MvU!9gj9|Vu*<3&&v+rMQ^iSP9NNz*dKXw1~X
zX;0$mqpXbnR!UMLyUig$R};4e)s^;sj)pL*+yIr;=12VG@;4TP)?fz!OV}W;d_Lh;
zBc(@DuQ9o{Dgvcjc8}g*7SI@2>j^3?p&X3U>}U(Kz5xvwc6(wmc;>?kLtjz%{q-KD
z3d1}TUfxM@J^xEw=DbcS!cVt%w<4i=5eO`w`M;SOksUjgu6{A}?s;p3D&3DDP~-Pk
zz3VwMC3|dIG;KUP-;7&Ktfl!w`@8yP&l3V2I0F1&)Pe}e$)PE9uWkz)dyp68UIU%g
z8YWyOt+T5|c3L5>;QcME1&M(LBU7^wq_|obe(iP#a&gRq$)8AxtrX#W;-aWkIL_)n
z&|1GZlaNn-wOwv*=9$9<+d?nbqno}7;lk>C;aGg$IuIKj^%QBc_q)w6ZBD0KT6?%z
zi3u0EUyj0EUR;tK><|B=9#I?uTg1!9#l_C=b#Z$Rg5O4lJ^tj_muKX`>1J81u8c+O
zzlSh`+u-5bc=+p`k4b$<;ShTRAR(R1$HlRXt*wue_+5=q5m5J+akCyf{JPwOWZMM<
z$le}5oYVwQqG)@rTvUJ2#@4dc+xJi+E^P+EfI3ct1(N30~sgsE(fr5-8Co9KZU-$U*_>NgW(1b!PMB4e}J4)Kk
z3H5S>8*?^l_x$q1MWgkx3i)})htKd9g)W!mzJc-iB_ukSLqkJnw$M*c)`#h!b3x8G
z$|pJ~&i~E&XJ}PmgD4xidulwy$W^WQ0Ys}XRbohS_nV;lXhER)cH;)`^h{3~+pFu)
zpDnD57UI^^whtjjB#X)9)yT8;ToZB^Jxq*^#ei5(2Y3~G|5V?YRngpdwr;azkbCh#>Ycc
z7=mNv)X_IP3GvmzQnR%tTNM?Ixy_3od{7QVMYt-i1sGz`&!peiG8w`nHw>^Am
zQzfN@c)m-psGIKLPksEGMl@6$^eaQ#9?{C5H}5
zs_!hJ7IJcOpd6KFTU=E92_3%%9y5dgSDkTSsgu=VK13!3=bLLX)07EvaIP>C^FF_x
zcUS>u+)<2TAh-=>#w^eqn#)RK2Yds1=JNrrVa?873PXOD!)VKFiU?Z*tA|J*pY8woy@q<4Dp#(E6+yF5=kxGOyB;~m&sSeZBXl@~{Ch!@?Naw;
z2-|NHo`0?ok3MP8d8S>d1
zB?$N*J*zn(*`GfbE%l?Nru8xUD}=HB;y>u%yj^m2b(LSw)2P9*K2K%)<+nhqCr%Mt
zi%NR$?(ykd*64ljS+L?*!9pZ92F)8NjTA0KPqxb&E2!cS6A}S$9vZGxxkM#D#R+Ud
z1SnKe!TY@*32tdjVtC^;@?XGe)>r%F_4k>ZM&WRM5B`Y2d>4d637>O_hnpOJ>$ww^
z8U`;4&Vi%tDWf(NU2tIx1AHNI#A(+Pmn8i{q6HCXjisiff{N&?IqLBr!@Zl@CYz=*
zBmR1>zZcl#O>%M4lEqiM;smG@BlZeSP4cGfSq!2X5H+8M8?C1%49UM&O)7C~YTgRb
zLNG7%4aO}ZEVfCZ+Ot&K`#mO@QHHk`l#qDdA1`X#cOIPXg_-}oI#6}}@?&<wYXN=>E^giJ0O%>*ME#Q+)gOgdo#{eUbiRx9!xw7f7vKGdS7OVq`|BVb&&(`%|n-
zFUSZGo9cjnotM>h_npw?`Ra52b_yjO>#p&3pM8pt8hnz1su=Wr?*L)Eoqk(pR>ldx
zJDMpYBH0568f8&ur_fAHl$;2rsMyf(c(s>D^g9MjgoSrex_F05V^~3cFHQVWy>#&GD>0QOd`iX#
zc{nT_Y|dyCC@hS@i;J`Pwt+CA2Z}^^(sKzeP5<(jaIW$<`K-4)L2j!7VVyQp1z>M|
z?DJeaA{kZw+rt#PiQ9WMv@1atO`I5t^w+OZ#;&})4a2T7*u$2+7?g$8qI8(^a%fTk
zHoAipC3!laf&4GN8ZEj)fC#{;m61)
zKUEEiV%Z;y}w-XS`>ShwR8bM2>0RklcYJ#2e-vuSkJeY`yB6Ika?vj
z02JZr<>-Wacz6i9p-A{C!BDF$UU5;twOv%Rd5I)pcH~~goOk~8X
z-@hVsr?kjl6jZK$K_B{Tt>uwE!A)Pa_wSjrc49gZQVO5HMFCwPn&9apz0AvRqOM_H
z9JTOWT?PKeFGQrvKi8r&i$iq0)DnpjC@@f>fFJOq6ke~r_8qv7#F<{TVg5d$%SoAA
zSfHimydLjB;MP2MA|Yj&s9D}fWE`%oYe;`<{Meph-}%k?(`cVmg0k|j$nXDp`HjF1
z)@O111A1*hnj#-Av74QmvRo`^Ib70IQ*%B%Zeikg^Sj)d7*x#ORUu@1375|#g2Y)s
zX!raGW+$&)3+Z*9slPCkS<2-(J#|+eD+p-|3+G00d;Ys>g4)`PUzx|1qKy@ZbhT=R1BDp4zRf=>;9ey2#=V=Bw%ZXRr}+5v@Gj$xzpImaz+KHKD~oA#
z0VoiFBO~^1PvH42?f3HAThhL!$7d
zrKKH=W_QZM5)h~xj!(SfrdPNA$Unp0C5{pcKM!6Q!L%;zlVEaEK2
zT#Gm(c7!o|NJQ(21_J{X;2uk!aNBpUhXEe5xCOE8D6`s?>*1@JdfR<#F5{{PLq_@A_k|FguwfipETGlPX7-2<-jG3``YUbhH~q9U`(
zs~KBRdNeO7(Q9W;7jm)kl9B?F2h5a~GDRlHx!|)o)8dv<(UNul?CTpI2GBZ4bJN@$
z!4_%#$Gkt0EM+TG3f;uAFvNBEeEoF
zYATMLD4Z%L=1V_6rQl12wHkG;mH8VfseKyPQb6casrd*V_JikufB;Bb3WUH?ZjS#2
zKMdYGAR%i1xYqd+ve)YzRUuOc(FW=PYE)9;@gJk&!!OdH|Q@$6DcF
z+)wtm$2PV*Z%rq};83hqD>Hy@@H#C$nZvU`yayV7X?Yo(R=$DI_DdSf@A3Ho{m&^b
z4o;-p%i1xIASWldqL&=CJ7qf9>UQMH&H?UbEIL!E7?9Tt^jU5KJD5NV&*rv1RmEV`E=4N?B+~NQ6kDp!RGy6Yd`Y
zWf@=g(XRC54heUGKHT2UfCPzs`>kdaP$|F0#+H=OMD_NXK?2C=y%_aW@cBMnOBMe5
zDXH%tX1NawTDqvj0`9BFi#9_e6KUn?SRdbf`I8D@M}E%>Q-#Y*i|5o4KT?yDC|_Ny
zSM8}snm1;c^@LJ}q5fUZb~B$yX9M+O<`lqN5ml1m7#SX({`r&qV-2_BXQ8oT*&i&l
zw_xX&tNAsV8K4M$waSwD5rP8?!21DWPb@ehQTNhtR(>`W1Ahp4>AIS-GRu-}NGu0OM-xa9#R>4VK_m+!
zAhtNa`t1bxGQIGqr0*3JB=|-JZ^&ptEz9F_cS|zP)y?T>sw%4@Y6PU-0W1(Cx{!fV
zH$HbWwKX`|2JX1lwpLFs55d?d23kgM0@Ao*zg!^zlsj{93c4vSDW;~OF8=tT!EUzP
zY8OBw0-hecWDHDOiAiD|;0+B8?d|pT^(`2@(8OQ9QmN)bP##}a%=E8cM1inL@!;?a
zI9@uLnP+S0q0nEeiq&VG=BMU%kznd{1FYEl(mCJ0rDvoCJ9^6U>MxplDo~`kRj~K>
z_h6qRIz#FrKuE1;&U+G6IEIDUtx;8;4#8FSOCxV-6fpO0XjJ7Pa1AurAnCv|f+CXr
zvYLpi{-1HTA`updFrbF7fD%As*k2d|BwddfTm<6n_Xk)Qk*h!Zzfa2lS0Y*B?o#-H
z93jx%2Q4D_ow2rMtu$46DQ%ipVM6|+pn{Mg#J~(Y>-5Yt1pwY|x`a-IO^W^|^Nbf_
zuI0lbD4-y*k!O|%?>ZQ?sGM*l%_s`sz2a&-jS~Vgjy1A4>hz#Qx!4es;>9rdZ`7g1
z6f}F;8vg%!&HoSmuRM7U2}EH=0Qx@9%$L8ydNJHpY1OXudBO8Ej~q@?1YGy9cXCOf
z=Ek(!3l<0kh}wO+$AS`y7x(=Cy~dFld=#eum+Ex^`~hI?h)vlfi-3aL9v;TOf*Wv)
z4W#-FcnxhBHTbcqm4pbXyTAWe!|kEq-<9@xxzb;029gVo{|CeT|Gegiu7sB~HF4p3
z;v>L7fu7;Z8ls$AN|kwYveeQBpt*;);9+i_93h)r^ZT*h12zQ+1wzU)9D^|=f}Z^d
z|JIyfW#&w<5}o;h#2I+|wiL+%1(}2M?^9@^4W6{Vxs9E%-Ph{si2@O^$0S!a>FUEJ
zYfH;TE!~lm#UDSyqtpV$lTr9LHWQ%_4h|jB!R7N5p`@jy6`+u<-_1${F;rl4#x?tO
zDNf`7RZZJOm%nWQ35REC`QhOX=nKdrcTu>)zJH$p=O8&PjWVbybq@ZtGpe-u+n+2z
zu6XqEytbMu80ZK`sLbXvlRcWNg1)a5
z23@i0n1eAO%T-ZP&+b@&H!!eSJ&QujVeu=S&DPeo4T#BAvLnL!SVC@vkyt!uk+$dMI3S4zTUhonb?-KNQ(J$iJsyOZHE7n~YGw@+`z|
zkBtOb084xo5rwG`^$jMBGAiqo+GdVdD~L8Nd=xe@F@=VPLaS-0@rO)L%%mUcZF9aZ
zmo6v*&PH%5IYe5E*EhR26Q2_*af7fZ#l-TXKd-z({iq9?wEz!F4)F=tAMI|ZIUfYW
z(!$_Bpf^!T%gFQ?Qi0bX*w%k5DuOXSX1|%DlrcEx&OnTc)JlK3pOh#vgW!-Y77GTx
zL{_Nldi>HYY66Vfd~p1!shJd)^aH8<7J%v&7c7+K
z$amBHs=q~n!5kcXbu!Dq$%I00X>R@>Un?vWgV6QiwmBrb!+RwIZ691fVoU3xO-e
zC1j{871og?{&LbU<&cUuSyy0CmxpLk(`SQJW8vr5b)8@}VrOSJ!^Z4}NmZ9uBF9KD
z!GKY&ZvVH(=HPreKmE_t_T=yq5i-y$nnAO;kPv9Zu?a+}<^4t(s6=YzD^4js<&|mS
zWYOR)WGJVr`FJFm!JOaL(=)U5M1(hr8wSWanYfp1_|JP#oioC>3V#RHY++7XOT>cF$REY
zzqrF@V6#l$fYh)u-tDld1fBNzr(MbgB27Ej~Kgurz$3jmZ)VcI#cM$9j5C_sbp%YT29``UYp_a$BNPe>gsAJ6}C#yM1#_#=(tnqY?w4
zaZn#@4u_F~hu0_0Zr>BuzA9he+cLGZ%+TRAO-vrbe1pir0UGWt?%5ZB7=gXBGi{k^
zAa_^>yqT9Kb8~BRjYR)e@s}@7M~AVnPJ7_xvMWo|Rn7@uVK9%1FB{{OG|tpvDEYN
zI|wv$UftL-Usx4P_gh^(~%{5d~>XrzXR0PjOqN)nOiJQo2
z?4;EYl2cPt%i|mGTpzDyQz8gq9{c7bi1J3PAn~N73vCqk_
z0yyyy5)#@CSSy%1pUW%AZJKB`p>Z0jF&lI967>T{
z;L&SqX-T`qBA-HSlZZ~=}tjJGTR0Sk4Z-xA`$
z&G7?eXFx$mL9`i>D2H~Tm><(V?GCL;)<*1Z*=h%;L@aD#)tml=UfI}TsZ+7!;(5%N
zBj%vEw0Y381HmU{N(Wj%E>$ux_zjd(V=i_Eq{5vt_K#oJ+J|;hA?6_(f2~f{mX;zw
zuha(zgEcYs3N)vIwG@mSRN{~RkhbRAXZS&K*~Q6)iIy?AI$)gLevWaDCf@ev?ihHR
zfW+9r&x$l9TJg(4SXzLN`DTfkb>=wf54m|D`1b82!Pm7`m*f6d3DO*S_G`Tb1@(Iq
zTPkDOoFm_t2$*@PDs?WM&U-^xZ5=E>G2`VLBiy=snTma8i5&ZN;_GwojEvl`oOf*e
z&7M@q6DU?^XDtYa=@qlI7rOr?(qKo
z>vnw>#R$Bf~8m1%wJy{A{H$=JNl1j~Z@EV(m^KW4Cq
zlMIFfvnq|e#Y!t@CWX!};5vJe(V?kYRinI;RGhE4%rIO5&NFK#
zo!_Z}f^nnYWr+!=#XPKmTW7fJE4paXXz8){Xnyso%5qJ@s8m*YhwGpb!PMIWM-F!uJBr^VSq}
z3WPzwsKH4IEg4CyBPpm4+;&l+P)SA`?H=b}zv3BD>qZ?cwbYju1dc66C|?0(+4!K&
z2w}x-y&J&)I2vEf4Ae(}|0@!U1x{(9Fb^LO$S!(=83MHAs>Yv*m-K*UF%>QJaLDk1
z>+M@luXB^3=l(x@lg@yr6cufRg~6dCIaeDF%@cw4s2*rjM_hLD$;mj>r|Wxfyk-w^?jqe=Koc>Vh)aI1BP2Xr
zid=~FZE!`PwWXPHPgTV>z$Gfl!%S{|dIFBI7N5V#X5G;HyQmJC>aSSM9cJ&y#WN&~
zkxgLqxC9LW7{DJ{){f%ryxX
zg9t^{iv$sl1z6A9Gvkp|CT$Y9{$x(g{adgVlMp~Z*3%~@PNsVD7!i3*gfs>XiPfl+
z3paMqMe1{P@FurAeWl6ijkTqPgc7$#IvAl)%8rqrQ>j3KK1TREwlO88*_?w@mFf?3
z_YQ;R!p62gIn+t$@g{9w*lRQXU~bpCx~hVsp4_A(IvxrlP8<-I{f=&2QbS9q4-x$v
zo{g=tZJ_`AyCZa?IR>C05O#t7J|nVOypXfc<1L$wI?h*AgIEg<&TDNi;QaaGMMUjQ
z3_`57sxuVjsR#%P?2hG(;kcKu@>1(QMje}qr|VaCuE({1v%&w)5G}!aP%IT((HlZV
zvkYhstJ-xiro^FA%-D&VDipR9tR!p@6#k=ih19{=U}u%!UpHhPZ=j<+Io1rgsm1jq
zGG88p=`vqtX+V7WZig&7mr)3##Pyj5Y3svAPW^CzXpBJR1_mOJ%Pw+o9Fs$UHbpBp
z5(IK&^Zw*qA
zf|iz6CN;qjE1j6Hh0AQ0n`GCb;!j{_hw=5y7d*8lM~hI;H|~0u%tAbzmN*)ik&$aY
zZv3hdXM}@;<1tGnvXyE5cpMP&a_%Gl4wV~P)%)IvuoqfPl?dd1R3dT7zgf
z1!P+yw&y}RUV&En0R)otscD{_Hm?K1;%dNZ@J0Ra2*k8hoMny1sH6BavdmH_y_Z@%
zC3YJrs@<>F>&1D)zBRx`<6hSm+3NlM>+Xe(K=G#)Bc52D?1N2s4xc54o{iQ8$Qggv
z8k`$ubDg0@ws43CNmN>5LTf0of@4bQ=xxil-_RjiBP(MV-I8=A5>-@Ke>kB>gx38;
zC+tme8=R6Y+9)X499wRZVffg0+BI_c_MAcl=Gw?d@UI?1`5S(%cUs^CK?dQ6@1aex
z#Duhmi`A`hZ`%iV0K4S14l&Z_e$}+FS|Lb
zSxggV
zff0nh8+c4f=B#){46aKCidl~CyOLtK022VJO+mfH6w+=qmF&2{5WfTtE?!A)1qdY_cm-Ehl&gM49@f~dWUjbL^_|SOaksI>A@%)+DZDEnkGxbD6`;~E
zU~*?>RKlcvMdX0>%9sdY;~{076Pn90*PufS4}L&vtk()1AOF=UoE>p5Q~((*YrJ4W
z@bWZ}wqVY(nOl4*H6#_RzX8T$U()HuA*FYNVkr>lucL0G>c
zJn(Z^rP0w&$*Bh_LU-ZGtR@G6>R$Vam?p3@Cnu<*!~AP+8f=YM_&PS*974e&+!N4F%Z6rWFK0>`6iKib1&}hZcam^c>xd4_`4y(D-X8=6=WZ@1dt9Ee%bJl!Z*W3;V}BMp+e(S
z#aQA$_nt~J_2~~lj5`jaJKkNSCs2Bsnf0Vg`SPrTs~_%h%X)Ql@(q_oGGVg!>sm89
zHt|?hk^^w=B`){+oJ52Aw7WxhG)nsln|Z>YK{jSV`CVV{Z`u3pGWIXHDn~ea0JV+Q}aI-sKY}8o5`Dz~k_g=)Jxu_=kj;@>eP9GaoG^Opq
z+R5iddbsVnkAHq2laNY!T9g(B1kaF>DT0NSX&{VyrNW%O;nv+BYN|Zrw|vEyyi5nX*aVq-6VO=!IYR#CK2%75>fFCm5)ocRQz|fFdo=V9S)fYB8B>ef>iWttoRYM%yuf$PoC?
zJ>85$Kyarx{{^j=XcfLf(b>GB*|7*l#=QfdP%8yd2HUUgdRhkLSoyEY>y0pNq!m)#
z(M#{pW6;cA%m~2h_-|Uuf0Iu(4J}GL!M_O~YavDtzE`@WBp{E>$tfJmpSt)@Is$$$
z`N`z3p*Tgpl9q-JqwiK>!ROzDn2rAb(NC^EPv9!*6nr$IdRD49{D1V5a|TM7e4!Wp
zBchjvdeb<5ueb4CyT{7oW15BM%Wt8M6t6GPyA8~R9NazZOa((?fOxYdo^e@_s;icQ
z`|GpC=F7)Ood?7IGq%qF*ti#EEmOnTcg(os`Si
zj(6c}8S$fnixyJ|h=_=B;nT_TvXt=jx>AY)vqMozI(lj+do7=dhygp85Coa7>1b({
zcC341V6bkpspSAve!0PJ1;jOH8X7h?H$mcuoSRbUJa9(!5(yC*84WX
ztJRRzC%ZM@AKd}QoTKRl%)sCQV{}dz3&X(sA)|#CF+L&T#aL@72CJJ>#$oKY-7&A*
zTjgwT?6#g44jN#hp7*cBp#B5#jf~WCd{NBi1Nt=R95~^!04eR>ZNIz99UE{Vu!u1)
zSK@^2mRm5HIu8f;BEHK51!eNg0hWQDzSZsIzDrTaI7wnY6JX~mgm&=z(Nkx@`KZlq
z>-%<5k*3@@0d&p1*PmlBu@8eqlmq_y)ZWVKeo@mfThNbWYbe2VFqY&Wl9JgG^D~>S
zJ`iEKR=}Bg&eFt#)lTk>k~wSjpopQNtB@R!l6k|BP8Oznp~7}IF@S%tE!zv`N+`aU
z>8VBQ1PR*F52?&iz^ZsXEJ4r4#wB@f!0UE!z2&Q>cH7});d{ho8|Xfk&ZRI%D!yEL
z)5`8}7abEbWz@_Qw|BY+Z%C-A=V^u>pFOIL)6miW=*?li9T6RA^7HR-K(^+Jo}%JJ
zZ|}Qtdx`gpiE1b4$YA5F`*jR@fuRzgy@p{M#^TNq6L-Ye+=`xGjFU?=P7=l8lMEsQ
zo>k%)77|)_PHm(_u}5@F^ap#5ALOeo^0s94Kt_$F)^+^krpjY%yK;d+qw#Q+r1-*$
zHcp`va=
zQLWgmo{fnBkj&qC#)S0T+_(_|;Ltk{&7_wiU(L=t2<5hxJZG*&8u}(=
zlYW}ZvIj|YgHBKEH0;-CI?6IK5nyzoJt5Zzuv8145@9tcPs4e)x33F;I61`xko#(f
z)*+$!ZtrR>2i85-F(?OyhF+mGN?h&&^s|@-POV2Zi6EIe2q<`=67B!0rPP@M`UHpy
z#N9X?j^b~NS_gAx8Y)eKCI?8T0)Pkbf@}`Ahwc((7V|{+aV;{YiSO@jZEm+vgY`dW
zOCb~AuP6#f)Z-J3=d$lDllqBwb(8BfTEX&)v+y`?{{YD150Y!ztZsIIBnTXU;dU1=
z$1E<)DQxG+$UaUGjH?J+>GT1C_n}YE)Olj?$vB)4Jjfp}@Fk
zGk@~5`Mte9SucgECQy#Gax=X%!s7`R?`x_jD35sdW;xtr7=2WqMW96uXw};h&)u9Z
zYpN-KdYqvndj0VOAf1r}FLmo+xr10d7)sUe(u4i$>CYNPg2iy~t5#3X1vEjwrxJzz
zn#$5^$Oj1};Yf(Tw-9x+5hkMoVz=uNe?i?v?dS!X!&_8^K6
z&ido2qi~Ns{HJ-s;)3q}8g-9ph9CAa0{gB3YkhX^862#qjFobZ5*KGHp!oUtxNrm!
zjj!T2#6oRWYN1uy%pP31;Lu`Fnh!1PmAb!X^MiCkeh3-*77?5t`ZmvKwBoJsksL9!5
zINyxMKgR|H)LjyKdAN(TO<|&c|G}oHLI{saoXKPN`KlF}ln*OALBaheC69Z5jWM3u
zLizoc;c}<3Kj&ab4*VP^T>QTX{T1~a?s*QJidWDKD3A4U-pk7;
zk5|7!ot|IL3c=r8YIaY+R`Esvqprgln`yyF2dx~SZuYouYRtSrVahC(Wxf)?t7D~-
zwMG*eL1wULlJP^*G%#TH8INVPMuDgjwd#oclAKV=`uvVUn-T)e>~>z%7o}jtcxh*um%vDoaKpHc+@!j<#mKHz*7+d
z!z@ba(^4#my5r2>b4UVDO{(6S+p-tNS#6QFzQK;jc~82NLczD3CC
zLMp@K;EJBJS#GH}@1Zs+e8};62t_8iT8k43`|r_TR4hz@VWks<_T=bmL7HS>oA)la`j
zdl8u?`h*t9kS*CUTW8$^yHA0P5M-T>ZjQIMuXS7wb
z5vz&8g_PfB<&u5=i&T!R&hS7Bx8-eT$Be5CXQk?yOL&5eOUJ?zCsAy)ABy_g+OU
z5Q1_KY!hC@(e!UygRv5b&Rp9UJAib#2_n?_MTu4Fuwe_$9`#pfpuY71lOt@k^9f*U
zpKHxVk|Qut2+vA5^YNSwN$u^M8xTxKL#^pLTpe1?3}ZwvL!2n}qlyo$*?`6P@b^xN
zOR;g{l{Hj`+cQ9^Ebmlk)OxMOZEb2i-5(7D*zIf~yi;6J-uCa`zrjccaE1)H5bFhV
zbte(9e=ULZ5=I1ljnhHExPEhYl`6u`D`n7?109tOA16>f4h$hk%6u!EaLv!e$hd9r
zcbJ8C0lcoK-~1v**~
zoq)ta;n2C}RtxtWn4EN2+#EnHD9k8>U26vzNE>x^qc+i+bQTIEj=+^*NL&_u0s$j`
z8%!0X@cAFcea+I?b@laZUq@O-(FlsVr`!LoL&{ef)+ZZ~1N)dg6
zaXScn-#_3Xz@x&7nxTsPz&5!i3?g(*ve-&+2ZCrb%PB|m+UcKuKi-u$Li(v0LPdd+
zk@N4Fh!Dc)P+v3{R-rz9AGgJ~!N$1&KWfvMX
zlmNsLrv^gO+0G!RvDi$_xuH_?9fv>^
zF7x&~fmJX~sHjU8$k%U;kG0$ZB2fY&fs3ye{fFa>>leh_6A9G>iffCU(xB=oeM1VmM6vA(MYjs`6RfSN<<
zN3TNnE9PdvAE2g=mu@g{O|}@S>T5$sPfX#gbKUw5hK3g4f|GYfRz?qLuhB$R1wJz2
z$x@TVsecvXA|FsA5s}4S7GaC!vl~Pt#5vog{v8d-8P=u&x^91o+ndWSvSw_Y8+%(k
zybqwM_g49?rY((8FT8#gj2f82d{ZKyPIh9~oLqOxGKa~s0g3~q2!i_h9E{pg1O{Bu
z-ZPZAnlWIR>){zJS!#A1F2cEo)kSR_eME4Ez)QDqZ9bbO%tJH$AV$)NrHUvEC5Qhz
zdZw!n-FSXIQO^E1T4dSjt|n#;CR-lP_TI>9N)oIl4io%%;S8$8mN9Oc1Y|Opo2YfI
zY>2}lZS$I6bPqO)h
zKda(^Xg1c$-UOqXtNQ&VrPYg#NU;8F>XRqUAyI&sz9|9gvB^z3d*Lij@h^$s+Dkq6
zhL^tUvv@mwJ2<14!UA)1qH25!uwO3=AYPCqSE;wX+W>$
zi5ASI`JWv0B+RUK%`2`V8o71^aEz7=ult8N4@UlP0XJE3)r%49e@Q;7=aA4tB(9#&
z#%J{36%tj}Wd^v|ETV<<-bq$m{S1)>U-m58_Lcq3WXpY8)nWOjKLf|(n=sgbquvRz$ze4#0
zgX3#2cd@xxdn>I?CItyH8nYfle^%E*b(kTfL38RMHg#H_GES!q2jOOlM1qAza#UB}
zsDu?Pm*$_wZOvHR>S9lcb83svA@+!4q3$lqyD%MjBXjP*W<879a!aP`ID31!6Tpwp
zY#%+f>?E0yVVY$?WEO?S>g*^Cx;pxWNJ~%eiPksP0c-F8J{Ac8v@!bN%Qg@kvwHxx
ziYgst7|JI{hjofsbYjA`Ki$H5;!qJZQwYi@l>OHb_wPWK?B(QbJDd!LMSa*CuNn^I
zb<#32`ulwN9$BU|s;j!%YscoR3Jg?FJmk~j498^WYat;7P~b&9UjDeB2O0G62MG)k
zEy+m3BTuV#%qLLF3?L&w1qbby0Wl!+TrvL@lsYX|%2SL{NVO6v>m9#x0JcFU?6({c
z!&zR3d;%dM;CTMh(_bhGawxb&>i*(~h&xGm%DEg>;b^vN_Y|B>Lg1PEg2
zcX>pC6{SKgS2T{yfDf$ZtXd6?jo{7f`L`!XH$NY&!K3k`%VqiGz5@j585!iGZ$})r
zb``h^{oAs#08-Z#3MsrQQ9@Hw(-S`CBq;>4cUq9r#>R%R4ZcZMS8il>14^VM;@TvzsO^Gey2v8uN4+Yz{Szl;CIC6`l%XKRXXgaiVPSiyR
zgoMm`3!q164M-qts^{f5`un&xl#9Og8BPv(x@XE1_S;;j{d;%TN3zoTiGerC{Vi?|X05?!E{I-&xzcv(JkpMt3~AKzzj`Hd5me$4CoTXeqFGAm#JhJ?^a
zmvEk9VO@clt03b0(hCRk)fSfG*8KeXC-cF|K_u*Yd{w4u_AxDo%{KG$a?mq|*X6g~
zOS~Q-4jx+dZ67W-zXD*&K>B#r*Dz)h5EMJWSmk6FG&FFCza42OlK!CTh1p;6PAtk?
z#`0jr>(BF_p*IF8Ka<&Sp#oA;WK*4Z+3(B_rdB^t<}M|
z`Rv224kdmDPL*9>b-#f$8Yl^@3VH!l<*83C+kOT;Gi~VzZ7f=
z;)~&9TBy2jxmivha{UbI?N(xRzk!x)dhM%OARYkC`OV_kk)n!YWNd5`i1J4PEXUT&
zVtt+KH!P5h0k9oImTHn7eQ=U?yj}Q|-%IBMdY3ZtvYQkbXc82-8;e79h-k<@0E}Nb)T0u4~H}@}xP$$@%GU{WmSAoIr#teAr>l=kqz>F6meJ}g|
z#=%Jp4ihs8%vhgk;8=)OMkNuUNT>oWXAmEG=6M?~Uf_hwEbaO|DmuD<6|e-+Z_aN*
zhr0^acB+X%ye{lDg}+Xn?N6N>1u|aN&srvGwX9O_#l(WF8r5-l6+aU>ZK`kY{3;Jh
zttvAn7#Ii&``J7`<`bRJ%Wi*X0f8LT)YMKNMZl8?*F!imgtD*z&CEE^
zJ;Xg@g4?K*nBQZ%ZCa6ga{eQv(T5NHBxq#_;!Ty6$Z*GiK3!*a`urpv=XW#uV^T>`
zbADy)Byl@O$Ik9I5K4*{YpAGbWN*%i0C@<^o&rM{XfY9!R8^X1N+93!Qd0h$S|z#J
z1*7s>)Iz#TSuj(3-1jy)DI1v&I4L)=cg!Mb&K%vkNCz@V9D)}qZP?lCn_OKLH@CpB
zNLJ_#Y{vxIi;K&oq@6uyf&y7(6Pt!x-q86cTD7K?57*c#YJKM_%(7Up6e8
zQjna^5+rqj)@0OyMNBv?V74j#;Rw{sU6F**N%-1FE^%|~%ayF~vch7C=;$_bh&AQ1
zXH+^U++KZ1VDJWT-fF!rWdnnpq^zj3DaBRJSgytzN#18-4FwZSb!{i
z4+*Ky2M2fW=HLcuBu*dS$I4>T4Zfig@VPLV3ydU`sUqOq~D
z!eF2`DExy?cv#dt#8Y&gP+qPwMhx1luVmRA)suReofDPlX}QZzo*_6TMPg%$Fc>4M
zA941X2a6V|u|Y;_z#dkXm4QykM6}*cyAfCg>#;q3qv3
zZq3vv`w}fEvP6oKWn>#`NLi9>gR(38Rx*ri#bhf>WwI{`k7cZ7Em@L;kPwr7$-e%s
z@jTz}`8~h${hsHXU;j+?r%l3djO4tmmYOp5I?HFowDDD_d>P-BfW*SKw5Aqynl6qGGylsjw!f&gObGZsT
z3bfCMGi88>R^9mT@Ya9$38?FS{`O9h^-PxcnEXTN8n)8CGkf;bB$*DN<0q^S`^`!B
z_M2CG&N!>6Od00WJFJ!1Q1dV%dL1v7)HnC{H+@SalUrZd(FB+=Amry)`%Hb^WH>f(
z;2_L(WV(Mprv8-p?3v}5N&&1pMG{OH(dZC1U3^ACCb*Rv&?jx}1=~Z}49&b&CdCdOjDBs>P2d~Zc6ulFwutU(Om6wL80R%q*
zxS%=E>%0o8|D+hiH#>CZ^l9tEoB~ZvZnxX8n{%UM-MMN}P{$?`N7k94JQ4&0^>^Ka
zQ*DmdY|g^N7XSw^cRw>b8U9KQt+qPXMJF)-Baj;Ex^zo3{ZIm~d~$}^mMo6~PBKh3
z&HqCkt~VblZ-+Wxo65qn1+5ow`RCcdU|;eDoA8u;C!nY-!U@sQAHznQcvfK>rbi$saU-MyvTG5>m_KDb)
z!72}=!=p!$`&Q-x=0EYiHwHrC{M1)m2oBxc)Sh%+OQ!H5wT$;AVS5m$z&hmzp_6N8
ztzhOtNJL}`0x4HoRagX$twh$Ne3uaM<541pz^E!eUzzEbm6gulSndI|LBTSW=(D~q
z$hl}?bXk%&0CM;kuc@cLs@{e)&ZrbQA&99oR;t5AM*}d=p{%sAI^(xK*$q>b7e}DN
z(da|&$A^bGf;f2=t*S{{<3|wo6Sn*kf6Q%f_Bi)`5)z*WX+t2z-E8I`wDqVnahDem
z6M>rp5XTa1X;jzL+V*jZm6fJ2=pnM`q_BYOLP^@xDP1+*=lyO4
zCV6Z->)w81yufo6`hcE0%RR6NyMD|uNKa2cE%CrKthVS$T4v^{ZpDfEBfv}c(a9D)
z<>YqbJ=QVM8cF;KHm=VkbwFr0Ho^sZoVqGJU^q~()Bv*T3q2gn#yWY2=yujgzYA(r
zeZX`mBZgQhj&4}av(x-PpmilIh{eiFHi~pb#{lMwq(lWSd`mFeb1mA!st~4%5TTxAo
zPN?WvA>GdgVm<0M!-Af=qnMvxAWvvUh^8B0#IVU4V`Wjm==&$qcLw&G#bBte;|#oT
z`Q#x3M$I`nrEl}Fc~#J%DRjE87?_NqvaXjOJ4b!QR7_%pLYb`zP`(FBPXL3U{8FAt
zCgG}b2tJcb6S+MFZe-djWW<22t&YIQHpf6-T6q7#dk|yFLlsShpgMK%{Jf?}NH{6$
z7fRbPk_)0(ZhWg484~z9@1_dKn%lpc`%pR+Zpp1FYxXW-Q^vXJ|4B!DD%hhV*nuyU
zG+px#rAVW|QW*bW&Yiax39%<3_DDZfdLaE&V$@C}1{maXMoX^H^OLf!ONzAMFx*Md
z!Kb33EP9L1nfDb88F8aIAgZlk2EN#b)HN6O-@TYr>s#%t_6*K`?lLog`}aPWcKjby%(dQ%yXYR)+RpX
zTs-uUTbR#FK4+0qrVgs`Eh@16?j|3V`6|;Ea}LXs$d%rR?$B-$4Z7OMDdF1Ca^4|*
zXeEARV)hX2bf;)El-dx~(g
z>~!?-GDTQ;_QVl`#MNYAvve%3u@o6|k?F>W=Xjv<)LA188@Qy+Md?nLq
zH`QCWEG?O+m}|{s!Q4>voWplh2TfdS9^E(M!z}`)O^5`5X7vfcaZe
z&bjB)CvWhFmVz@oDd!Dm&fZsFZ)xS!h>Pv4u=&`o7zp-wisSyx=6y)6Q2)KG%o|Bk)Wylif;xl!h{hXSa
z8JqYH3i9BFv>dl5A5+&Rh=eG{-{e$t9U_baDf}hFG(HOgZTL+fY7gwnVrVs!N&emAnKu8Ex&h#^2-jYcc!@Eu#~|-ZC=MM52MYcaEkuDQoLQ14jQM1#E_C)3GlT
zF|2^*AGNm$-w$jx*tIFN*!iYrC!ZufZ{pVw6c!5!%nrv0qiE3tp--P!bFSTlyN(MF
zBAQn0DlQ?HbG|L%^iwIHtzO#V+bc8Ozh9haE@!hRtQ`sDTTN~|n|9lxp%Dn-Z>(%T
z-#r#780M}%V|w-nHYxEbJ1gr?)NV%R4Z(|QW3xnceM$%RO2+s&{0kjMZ4Nwwk3#4H
zj{wAQzqs=rxqHc)tiJ4AV)V=NFFZ{pmMvpu*LcPE
zu`mjOxn=KzyAph_Qwb@m?GcDjIKV0*;GNE<+~}sivILhMHaZY42!ztXzl~V^(+h>2
z+p$d;LZq{};A9sUjt&mg~xrgr*~X+t0n*
zKiH|{dh`JYr&A&YrjzCFu)W^NM7A-dLL=CCbXq_7rMLk(qJqRR4f1VX8)-wx
z{3|!=pFW#^gX>`-0`F_(G4`(z8znBT0F?-aMJs&X$i
z022=Pr)Uu)VtFvAc5A+yl{(!3G#BVv(illM35uUk3`JFXX`oBl?IR
zfW1!HsEL1aQ4Lc|(2u;KBX1_eEI6{BIe?*S?q=3{|(D>dz$srW}=gah*qIUx+;v
zJ3vq2CNX_MNI;Jy$gb_$5w>sa2mTf8o7jJl$SJ+U7`N8EOywEi>WXkM4%9ST!$d`v(H6m>c`aR9I3z9Se+(S$ag~
zKH(xqoF_Zv|NY{MKw^t?6S*c~2+o5)?}-O+d&K^Jiq4=Vfce2jzvyF=zVuBTt{z0;
zFN4)58LaS5W&^0K60mZX#)sD@-~jERDvUDkkCIgi{Duz+<}{Rn3U5hBZ$2}$^j~kN
zcCG2Xo;TrlLwK9-?&y@4uDzB>Ye>eTC
zOF;LhA#7zOHDW7CFh(*+Uwv<}mB_HW8qluuxTvP25RcCocN@9_J9*HKs~X(@;B3ni
z5;5=HtNB%<+3*4R$dVY=Horr@tH16~OqLK@eA1av$6d2wEi?GYJ||{uJ=Ff%X!Qqs
zTM~L8s_uraR~gK$UHrqL2X=-ak595J!`osUs~m^7ezYGQyqUc1U%kA%9PIgL2
zY1SXid?Yfp;~s&ROH!$~ie%sGbzlBFki>bHvtGTD@M>B*ZkTtY+Oyv4Qb90Rsh7J#
z$h;rZ@p~KVi`@=8zt@f4*Ozjgd$U{p9w@kF(a%vYo0v^WB`19rE^i+6qeeEk>u64cL1Nr0FH(ChB5?(WZDg2#Rhd=InP
z7ps?rRdxtis&(DohrDzz{>qgr4;1v>Bg4Y(N^o-rSEJhEy)eEY(>ZZ9t{wS4@D|D55W1+Z@;a=a)#KgH?B4VllfYreay^{d}OYdZ2&Pc$)%
zmMK(N7@lv8Ic1O~=$EiH;XBtd)IoOuo?$&_#zo`FMg{z<9KZYHTex`9#NsmCIf-Jp
znrheY65WaZGZl|Yx2<1vo
zE~DiDxc*a2Nz=)Ui!>vcTz=n*FB&ew{TfQ+^bEV=`6
z0nv|eP&NmnXb8hGPEV4UgvIcL)?6_8tJC@7?fX0~WpsGl9%H@5BXD8XZmUjOB4}%y
zdRvzMSJxEe#|UOexNSm<%GrnEdw{WzuE8&pV~q0&&4KE<}Mv@76J7FLmk1U
zmIgj(9Ncxk2R(!?2;)&W@$Zlx|IA^b>AYyE+Yv{`=3!ZK5}g5_T5s$RhdXbFohAIP
zv7<}}58$(Yt}j8}+4E!0Njn2gghVb(nr<{K|6FXsZA@uTvDi8&f@Nc{MMXs*4Y$Z`
z5fV6Aq?P$HTBN7^qJC9XG6Y@kK|X=^{+R9P3looJda{^}$DS(O_-5=St4ZrZTUR?cLNLa=YM0LY-1Ko{;Z1i6
O_@{x^QF*Oof&CwFl{dx!
literal 70970
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