mirror of
https://github.com/Picocrypt/CLI.git
synced 2026-03-11 14:44:23 +00:00
Initial commit
This commit is contained in:
parent
7154a4b859
commit
7a8a9af5e0
5 changed files with 788 additions and 0 deletions
32
.github/workflows/codeql-analysis.yml
vendored
Normal file
32
.github/workflows/codeql-analysis.yml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
name: "CodeQL"
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "picocrypt/*.go"
|
||||
- "picocrypt/go.mod"
|
||||
- "picocrypt/go.sum"
|
||||
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@v4
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
34
picocrypt/README.md
Normal file
34
picocrypt/README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Installation
|
||||
If you don't have Go installed, download it from <a href="https://go.dev/dl/">here</a> or install it from your package manager. Then, run the command below:
|
||||
```
|
||||
go install github.com/HACKERALERT/Picocrypt/cli/v2/picocrypt@latest
|
||||
```
|
||||
You should now be able to run `picocrypt` in your terminal. If not, run `export PATH=$PATH:$(go env GOPATH)/bin` and try again.
|
||||
# Usage
|
||||
```
|
||||
C:\Users\Evan>picocrypt
|
||||
Usage: picocrypt <item1> [<item2> ...]
|
||||
Items: can be files, folders, or globs
|
||||
Flags:
|
||||
-f (decryption) attempt to fix corruption
|
||||
-k (decryption) keep output even if corrupted
|
||||
-p (encryption) use paranoid mode
|
||||
-r (encryption) encode with Reed-Solomon
|
||||
```
|
||||
## Examples
|
||||
To encrypt a single file:
|
||||
```
|
||||
picocrypt secret.pdf
|
||||
```
|
||||
To encrypt all files in the current working directory:
|
||||
```
|
||||
picocrypt *
|
||||
```
|
||||
To encrypt all PNGs and JPGs with paranoid mode and Reed-Solomon:
|
||||
```
|
||||
picocrypt -p -r *.png *.jpg
|
||||
```
|
||||
To decrypt a volume:
|
||||
```
|
||||
picocrypt volume.pcv
|
||||
```
|
||||
17
picocrypt/go.mod
Normal file
17
picocrypt/go.mod
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
module github.com/HACKERALERT/Picocrypt/cli/v2/picocrypt
|
||||
|
||||
go 1.22.2
|
||||
|
||||
require (
|
||||
github.com/HACKERALERT/infectious v0.0.0-20240424200929-b9ce72346a19
|
||||
github.com/HACKERALERT/serpent v0.0.0-20210716182301-293b29869c66
|
||||
github.com/schollz/progressbar/v3 v3.14.2
|
||||
golang.org/x/crypto v0.22.0
|
||||
golang.org/x/term v0.19.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
golang.org/x/sys v0.19.1-0.20240416221847-9a28524796a5 // indirect
|
||||
)
|
||||
29
picocrypt/go.sum
Normal file
29
picocrypt/go.sum
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
github.com/HACKERALERT/infectious v0.0.0-20240424200929-b9ce72346a19 h1:C5t561XXXRJvdiluejbka36n+YaOB4XJuQIo+25hL1k=
|
||||
github.com/HACKERALERT/infectious v0.0.0-20240424200929-b9ce72346a19/go.mod h1:bTnpEk9zNS1sVKg5TRvLkuSEGVqH0+LRfcMurPtcJvY=
|
||||
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyMFIEa99ks=
|
||||
github.com/schollz/progressbar/v3 v3.14.2/go.mod h1:aQAZQnhF4JGFtRJiw/eobaXpsqpVQAftEQ+hLGXaRc4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.1-0.20240416221847-9a28524796a5 h1:0exPaeAtAlmNHCcRJc+hETS3/TcMV+yjoHhlp4+Ff3E=
|
||||
golang.org/x/sys v0.19.1-0.20240416221847-9a28524796a5/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
676
picocrypt/main.go
Normal file
676
picocrypt/main.go
Normal file
|
|
@ -0,0 +1,676 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"flag"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/HACKERALERT/infectious"
|
||||
"github.com/HACKERALERT/serpent"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"golang.org/x/crypto/chacha20"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var f *bool
|
||||
var k *bool
|
||||
var p *bool
|
||||
var r *bool
|
||||
var mode string
|
||||
|
||||
func parse() int {
|
||||
flag.Usage = func() {
|
||||
fmt.Println("Usage: picocrypt <item1> [<item2> ...]")
|
||||
fmt.Println("Items: can be files, folders, or globs")
|
||||
fmt.Println("Flags:")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
f = flag.Bool("f", false, "(decryption) attempt to fix corruption")
|
||||
k = flag.Bool("k", false, "(decryption) keep output even if corrupted")
|
||||
p = flag.Bool("p", false, "(encryption) use paranoid mode")
|
||||
r = flag.Bool("r", false, "(encryption) encode with Reed-Solomon")
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
flag.Usage()
|
||||
}
|
||||
for _, v := range flag.Args() {
|
||||
if strings.HasPrefix(v, "-") {
|
||||
fmt.Println("Flags must be provided before arguments!")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if flag.NArg() == 1 {
|
||||
if strings.HasSuffix(flag.Arg(0), ".pcv") {
|
||||
mode = "decrypt"
|
||||
} else {
|
||||
mode = "encrypt"
|
||||
}
|
||||
} else {
|
||||
mode = "encrypt"
|
||||
for _, v := range flag.Args() {
|
||||
if strings.HasSuffix(v, ".pcv") {
|
||||
fmt.Println("Multiple items must not contain volumes.")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
var password []byte
|
||||
var confirmp []byte
|
||||
var err error
|
||||
|
||||
func auth() int {
|
||||
if mode == "encrypt" {
|
||||
fmt.Print("Password: ")
|
||||
password, err = term.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
fmt.Println("Error reading password!")
|
||||
return 1
|
||||
}
|
||||
fmt.Print(strings.Repeat("*", len(password)), " | Confirm: ")
|
||||
confirmp, err = term.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
fmt.Println("Error reading password!")
|
||||
return 1
|
||||
}
|
||||
fmt.Println(strings.Repeat("*", len(confirmp)))
|
||||
if !bytes.Equal(password, confirmp) {
|
||||
fmt.Println("Passwords don't match!")
|
||||
return 1
|
||||
}
|
||||
} else {
|
||||
fmt.Print("Password: ")
|
||||
password, err = term.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
fmt.Println("Error reading password!")
|
||||
return 1
|
||||
}
|
||||
fmt.Println(strings.Repeat("*", len(password)))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
var pin string
|
||||
var pout string
|
||||
var pzip string
|
||||
var file *os.File
|
||||
var writer *zip.Writer
|
||||
var files []string
|
||||
var interrupted bool
|
||||
|
||||
func prepare() int {
|
||||
if mode == "decrypt" {
|
||||
pin = flag.Arg(0)
|
||||
pout = strings.TrimSuffix(pin, ".pcv")
|
||||
} else {
|
||||
stat, err := os.Stat(flag.Arg(0))
|
||||
if flag.NArg() == 1 && err == nil && !stat.IsDir() {
|
||||
pin = flag.Arg(0)
|
||||
pout = pin + ".pcv"
|
||||
} else {
|
||||
items := []string{}
|
||||
for _, v := range flag.Args() {
|
||||
if strings.Contains(v, "../") || strings.HasPrefix(v, "/") {
|
||||
fmt.Println("Cannot encrypt outside of current directory.")
|
||||
return 1
|
||||
}
|
||||
matches, err := filepath.Glob(v)
|
||||
if err != nil {
|
||||
fmt.Println("Invalid glob pattern:", v)
|
||||
return 1
|
||||
}
|
||||
items = append(items, matches...)
|
||||
}
|
||||
for _, v := range items {
|
||||
stat, err := os.Stat(v)
|
||||
if err != nil {
|
||||
fmt.Println("Cannot access input:", v)
|
||||
return 1
|
||||
}
|
||||
if !stat.IsDir() {
|
||||
files = append(files, v)
|
||||
} else {
|
||||
filepath.Walk(v, func(path string, _ os.FileInfo, _ error) error {
|
||||
stat, err := os.Stat(path)
|
||||
if err == nil && !stat.IsDir() {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(files) == 0 {
|
||||
fmt.Println("Nothing to encrypt!")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func compress() int {
|
||||
if files == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Println("Cannot get current working directory!")
|
||||
return 1
|
||||
}
|
||||
dir = filepath.ToSlash(dir)
|
||||
file, err = os.CreateTemp("", "picocrypt-cli-v2-*.tmp")
|
||||
if err != nil {
|
||||
fmt.Println("Cannot create temporary file!")
|
||||
return 1
|
||||
}
|
||||
pzip = file.Name()
|
||||
writer = zip.NewWriter(file)
|
||||
|
||||
for i, path := range files {
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
header, err := zip.FileInfoHeader(stat)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
abs, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
abs = filepath.ToSlash(abs)
|
||||
header.Name = strings.TrimPrefix(abs, dir)
|
||||
header.Name = strings.TrimPrefix(header.Name, "/")
|
||||
header.Method = zip.Deflate
|
||||
entry, err := writer.CreateHeader(header)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
fin, err := os.Open(path)
|
||||
if err != nil {
|
||||
writer.Close()
|
||||
file.Close()
|
||||
fmt.Println("Read access to input denied:", path)
|
||||
return 1
|
||||
}
|
||||
bar := progressbar.NewOptions(
|
||||
int(stat.Size()),
|
||||
progressbar.OptionClearOnFinish(),
|
||||
progressbar.OptionFullWidth(),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionUseIECUnits(true),
|
||||
progressbar.OptionSetDescription(
|
||||
fmt.Sprintf("Compressing [%d/%d]:", i+1, len(files)),
|
||||
),
|
||||
)
|
||||
_, err = io.Copy(io.MultiWriter(entry, bar), fin)
|
||||
fin.Close()
|
||||
if err != nil {
|
||||
if interrupted {
|
||||
time.Sleep(1 * time.Second)
|
||||
} else {
|
||||
writer.Close()
|
||||
file.Close()
|
||||
fmt.Println("Insufficient disk space!")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.Close()
|
||||
file.Close()
|
||||
pin = file.Name()
|
||||
pout = "encrypted-" + strconv.Itoa(int(time.Now().Unix())) + ".zip.pcv"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
var fin *os.File
|
||||
var fout *os.File
|
||||
var padded bool
|
||||
var salt []byte
|
||||
var hkdfSalt []byte
|
||||
var serpentIV []byte
|
||||
var nonce []byte
|
||||
var keyHash []byte
|
||||
var keyHashRef []byte
|
||||
var authTag []byte
|
||||
var key []byte
|
||||
var mac hash.Hash
|
||||
var MiB = 1 << 20
|
||||
var GiB = 1 << 30
|
||||
var rs5, _ = infectious.NewFEC(5, 15)
|
||||
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)
|
||||
|
||||
func rsEncode(rs *infectious.FEC, data []byte) []byte {
|
||||
res := make([]byte, rs.Total())
|
||||
rs.Encode(data, func(s infectious.Share) {
|
||||
res[s.Number] = s.Data[0]
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
func rsDecode(rs *infectious.FEC, data []byte, fast bool) ([]byte, error) {
|
||||
if rs.Total() == 136 && fast {
|
||||
return data[:128], nil
|
||||
}
|
||||
tmp := make([]infectious.Share, rs.Total())
|
||||
for i := 0; i < rs.Total(); i++ {
|
||||
tmp[i].Number = i
|
||||
tmp[i].Data = append(tmp[i].Data, 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
|
||||
}
|
||||
|
||||
func pad(data []byte) []byte {
|
||||
padLen := 128 - len(data)%128
|
||||
padding := bytes.Repeat([]byte{byte(padLen)}, padLen)
|
||||
return append(data, padding...)
|
||||
}
|
||||
|
||||
func unpad(data []byte) []byte {
|
||||
padLen := int(data[127])
|
||||
return data[:128-padLen]
|
||||
}
|
||||
|
||||
func work() int {
|
||||
fin, err = os.Open(pin)
|
||||
if err != nil {
|
||||
fmt.Println("Error accessing input file:", pin)
|
||||
return 1
|
||||
}
|
||||
_, err = os.Stat(pout)
|
||||
if err == nil {
|
||||
fmt.Println("Output file already exists!")
|
||||
return 1
|
||||
}
|
||||
fout, err = os.Create(pout)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating output file:", pout)
|
||||
return 1
|
||||
}
|
||||
stat, err := os.Stat(pin)
|
||||
if err != nil {
|
||||
fmt.Println("Error accessing input file:", pin)
|
||||
return 1
|
||||
}
|
||||
total := stat.Size()
|
||||
if mode == "decrypt" {
|
||||
total -= 789
|
||||
}
|
||||
|
||||
if mode == "encrypt" {
|
||||
errs := make([]error, 10)
|
||||
salt = make([]byte, 16)
|
||||
hkdfSalt = make([]byte, 32)
|
||||
serpentIV = make([]byte, 16)
|
||||
nonce = make([]byte, 24)
|
||||
_, errs[0] = fout.Write(rsEncode(rs5, []byte("v1.34")))
|
||||
_, errs[1] = fout.Write(rsEncode(rs5, []byte("00000")))
|
||||
flags := make([]byte, 5)
|
||||
if *p {
|
||||
flags[0] = 1
|
||||
}
|
||||
if *r {
|
||||
flags[3] = 1
|
||||
}
|
||||
if total%int64(MiB) >= int64(MiB)-128 {
|
||||
flags[4] = 1
|
||||
}
|
||||
_, errs[2] = fout.Write(rsEncode(rs5, flags))
|
||||
rand.Read(salt)
|
||||
rand.Read(hkdfSalt)
|
||||
rand.Read(serpentIV)
|
||||
rand.Read(nonce)
|
||||
_, errs[3] = fout.Write(rsEncode(rs16, salt))
|
||||
_, errs[4] = fout.Write(rsEncode(rs32, hkdfSalt))
|
||||
_, errs[5] = fout.Write(rsEncode(rs16, serpentIV))
|
||||
_, errs[6] = fout.Write(rsEncode(rs24, nonce))
|
||||
_, errs[7] = fout.Write(make([]byte, 192))
|
||||
_, errs[8] = fout.Write(make([]byte, 96))
|
||||
_, errs[9] = fout.Write(make([]byte, 192))
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("Insufficient disk space!")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errs := make([]error, 9)
|
||||
version := make([]byte, 15)
|
||||
fin.Read(version)
|
||||
_, errs[0] = rsDecode(rs5, version, !(*f))
|
||||
tmp := make([]byte, 15)
|
||||
fin.Read(tmp)
|
||||
tmp, errs[1] = rsDecode(rs5, tmp, !(*f))
|
||||
comments, _ := strconv.Atoi(string(tmp))
|
||||
fin.Read(make([]byte, comments*3))
|
||||
total -= int64(comments) * 3
|
||||
flags := make([]byte, 15)
|
||||
fin.Read(flags)
|
||||
flags, errs[2] = rsDecode(rs5, flags, !(*f))
|
||||
*p = flags[0] == 1
|
||||
*r = flags[3] == 1
|
||||
padded = flags[4] == 1
|
||||
if flags[1] == 1 {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("Keyfiles are not supported!")
|
||||
return 1
|
||||
}
|
||||
salt = make([]byte, 48)
|
||||
fin.Read(salt)
|
||||
salt, errs[3] = rsDecode(rs16, salt, !(*f))
|
||||
hkdfSalt = make([]byte, 96)
|
||||
fin.Read(hkdfSalt)
|
||||
hkdfSalt, errs[4] = rsDecode(rs32, hkdfSalt, !(*f))
|
||||
serpentIV = make([]byte, 48)
|
||||
fin.Read(serpentIV)
|
||||
serpentIV, errs[5] = rsDecode(rs16, serpentIV, !(*f))
|
||||
nonce = make([]byte, 72)
|
||||
fin.Read(nonce)
|
||||
nonce, errs[6] = rsDecode(rs24, nonce, !(*f))
|
||||
keyHashRef = make([]byte, 192)
|
||||
fin.Read(keyHashRef)
|
||||
keyHashRef, errs[7] = rsDecode(rs64, keyHashRef, !(*f))
|
||||
fin.Read(make([]byte, 96))
|
||||
authTag = make([]byte, 192)
|
||||
fin.Read(authTag)
|
||||
authTag, errs[8] = rsDecode(rs64, authTag, !(*f))
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("The volume header is irrecoverably damaged!")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if *p {
|
||||
key = argon2.IDKey(password, salt, 8, 1<<20, 8, 32)
|
||||
} else {
|
||||
key = argon2.IDKey(password, salt, 4, 1<<20, 4, 32)
|
||||
}
|
||||
tmp := sha3.New512()
|
||||
tmp.Write(key)
|
||||
keyHash = tmp.Sum(nil)
|
||||
if mode == "decrypt" {
|
||||
if !bytes.Equal(keyHash, keyHashRef) {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("Incorrect password!")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
done, counter := 0, 0
|
||||
chacha, _ := chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||
subkey := make([]byte, 32)
|
||||
hkdf := hkdf.New(sha3.New256, key, hkdfSalt, nil)
|
||||
hkdf.Read(subkey)
|
||||
if *p {
|
||||
mac = hmac.New(sha3.New512, subkey)
|
||||
} else {
|
||||
mac, _ = blake2b.New512(subkey)
|
||||
}
|
||||
serpentKey := make([]byte, 32)
|
||||
hkdf.Read(serpentKey)
|
||||
s, _ := serpent.NewCipher(serpentKey)
|
||||
serpent := cipher.NewCTR(s, serpentIV)
|
||||
|
||||
bar := progressbar.NewOptions(
|
||||
int(total),
|
||||
progressbar.OptionClearOnFinish(),
|
||||
progressbar.OptionFullWidth(),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionUseIECUnits(true),
|
||||
progressbar.OptionSetDescription(
|
||||
(func() string {
|
||||
if mode == "encrypt" {
|
||||
return "Encrypting:"
|
||||
}
|
||||
return "Decrypting:"
|
||||
})(),
|
||||
),
|
||||
)
|
||||
for {
|
||||
var src []byte
|
||||
if mode == "decrypt" && *r {
|
||||
src = make([]byte, MiB/128*136)
|
||||
} else {
|
||||
src = make([]byte, MiB)
|
||||
}
|
||||
size, err := fin.Read(src)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
src = src[:size]
|
||||
dst := make([]byte, len(src))
|
||||
bar.Write(src)
|
||||
|
||||
if mode == "encrypt" {
|
||||
if *p {
|
||||
serpent.XORKeyStream(dst, src)
|
||||
copy(src, dst)
|
||||
}
|
||||
chacha.XORKeyStream(dst, src)
|
||||
mac.Write(dst)
|
||||
if *r {
|
||||
copy(src, dst)
|
||||
dst = nil
|
||||
if len(src) == MiB {
|
||||
for i := 0; i < MiB; i += 128 {
|
||||
dst = append(dst, rsEncode(rs128, src[i:i+128])...)
|
||||
}
|
||||
} else {
|
||||
chunks := math.Floor(float64(len(src)) / 128)
|
||||
for i := 0; float64(i) < chunks; i++ {
|
||||
dst = append(dst, rsEncode(rs128, src[i*128:(i+1)*128])...)
|
||||
}
|
||||
dst = append(dst, rsEncode(rs128, pad(src[int(chunks*128):]))...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if *r {
|
||||
copy(dst, src)
|
||||
src = nil
|
||||
if len(dst) == MiB/128*136 {
|
||||
for i := 0; i < MiB/128*136; i += 136 {
|
||||
tmp, err := rsDecode(rs128, dst[i:i+136], !(*f))
|
||||
if err != nil {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("\nThe input file is irrecoverably damaged.")
|
||||
return 1
|
||||
}
|
||||
if i == MiB/128*136-136 && done+MiB/128*136 >= int(total) && padded {
|
||||
tmp = unpad(tmp)
|
||||
}
|
||||
src = append(src, tmp...)
|
||||
}
|
||||
} else {
|
||||
chunks := len(dst)/136 - 1
|
||||
for i := 0; i < chunks; i++ {
|
||||
tmp, err := rsDecode(rs128, dst[i*136:(i+1)*136], !(*f))
|
||||
if err != nil {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("\nThe input file is irrecoverably damaged.")
|
||||
return 1
|
||||
}
|
||||
src = append(src, tmp...)
|
||||
}
|
||||
tmp, err := rsDecode(rs128, dst[int(chunks)*136:], !(*f))
|
||||
if err != nil {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("\nThe input file is irrecoverably damaged.")
|
||||
return 1
|
||||
}
|
||||
src = append(src, unpad(tmp)...)
|
||||
}
|
||||
dst = make([]byte, len(src))
|
||||
}
|
||||
mac.Write(src)
|
||||
chacha.XORKeyStream(dst, src)
|
||||
if *p {
|
||||
copy(src, dst)
|
||||
serpent.XORKeyStream(dst, src)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = fout.Write(dst)
|
||||
if err != nil {
|
||||
if interrupted {
|
||||
time.Sleep(1 * time.Second)
|
||||
} else {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("\nInsufficient disk space!")
|
||||
return 1
|
||||
}
|
||||
}
|
||||
if mode == "decrypt" && *r {
|
||||
done += MiB / 128 * 136
|
||||
} else {
|
||||
done += MiB
|
||||
}
|
||||
|
||||
if counter >= 60*GiB {
|
||||
nonce = make([]byte, 24)
|
||||
hkdf.Read(nonce)
|
||||
chacha, _ = chacha20.NewUnauthenticatedCipher(key, nonce)
|
||||
serpentIV = make([]byte, 16)
|
||||
hkdf.Read(serpentIV)
|
||||
serpent = cipher.NewCTR(s, serpentIV)
|
||||
counter = 0
|
||||
}
|
||||
}
|
||||
|
||||
if mode == "encrypt" {
|
||||
fout.Seek(309, 0)
|
||||
fout.Write(rsEncode(rs64, keyHash))
|
||||
fout.Write(rsEncode(rs32, make([]byte, 32)))
|
||||
fout.Write(rsEncode(rs64, mac.Sum(nil)))
|
||||
} else {
|
||||
if !bytes.Equal(mac.Sum(nil), authTag) {
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
if *k {
|
||||
fmt.Println("\nThe modified output has been kept.")
|
||||
return 0
|
||||
} else {
|
||||
fmt.Println("\nThe input volume is damaged or modified!")
|
||||
if *r {
|
||||
fmt.Println("Fortunately, this volume is encoded with Reed-Solomon.")
|
||||
fmt.Println("Try again using the '-f' flag to repair the corruption.")
|
||||
}
|
||||
return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fin.Close()
|
||||
fout.Close()
|
||||
fmt.Println("Completed ->", fout.Name())
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
if parse() == 1 {
|
||||
os.Exit(1)
|
||||
}
|
||||
if auth() == 1 {
|
||||
os.Exit(1)
|
||||
}
|
||||
if prepare() == 1 {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
<-c
|
||||
interrupted = true
|
||||
if fin != nil {
|
||||
fin.Close()
|
||||
}
|
||||
if fout != nil {
|
||||
fmt.Print("\nSystem interrupt detected, cleaning up incomplete output: ")
|
||||
fout.Close()
|
||||
if err := os.Remove(fout.Name()); err == nil {
|
||||
fmt.Print("Success.")
|
||||
} else {
|
||||
fmt.Print("Failure.")
|
||||
}
|
||||
}
|
||||
if pzip != "" {
|
||||
fmt.Print("\nSystem interrupt detected, cleaning up temporary files: ")
|
||||
writer.Close()
|
||||
file.Close()
|
||||
if err := os.Remove(pzip); err == nil {
|
||||
fmt.Print("Success.")
|
||||
} else {
|
||||
fmt.Print("Failure.")
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
os.Exit(1)
|
||||
}()
|
||||
|
||||
if compress() == 1 {
|
||||
os.Remove(pzip)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
defer os.Remove(pzip)
|
||||
}
|
||||
if work() == 1 {
|
||||
if pzip != "" {
|
||||
os.Remove(pzip)
|
||||
}
|
||||
if fout != nil {
|
||||
os.Remove(fout.Name())
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue