From 30675a987b3ad3e9719f4f86c003eaa5a083879a Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Sat, 25 Nov 2023 14:41:58 +0100 Subject: [PATCH] 1st pub version --- .gitignore | 4 + Makefile | 89 +++++++++++++++++++ README.md | Bin 0 -> 8743 bytes crypto.go | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 11 +++ go.sum | 10 +++ main.go | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++ mkrel.sh | 65 ++++++++++++++ 8 files changed, 661 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 crypto.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100755 mkrel.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e805f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +releases +a +t +gowipe diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d25b893 --- /dev/null +++ b/Makefile @@ -0,0 +1,89 @@ +# Copyright © 2023 Thomas von Dein + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +# +# no need to modify anything below +tool = gowipe +VERSION = $(shell grep VERSION main.go | head -1 | cut -d '"' -f2) +archs = darwin freebsd linux windows +PREFIX = /usr/local +UID = root +GID = 0 +HAVE_POD := + +all: $(tool) buildlocal + +buildlocal: + CGO_LDFLAGS='-static' go build -tags osusergo,netgo -ldflags "-extldflags=-static" -o $(tool) + +install: buildlocal + install -d -o $(UID) -g $(GID) $(PREFIX)/bin + install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1 + install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/ + install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/ + +clean: + rm -rf $(tool) coverage.out + +test: + go test -v ./... + +singletest: + @echo "Call like this: ''make singletest TEST=TestPrepareColumns" + go test -run $(TEST) + +cover-report: + go test ./... -cover -coverprofile=coverage.out + go tool cover -html=coverage.out + +goupdate: + go get -t -u=patch ./... + +buildall: + ./mkrel.sh $(tool) $(VERSION) + +release: buildall + gh release create v$(VERSION) --generate-notes releases/* + +show-versions: buildlocal + @echo "### gowipe version:" + @./gowipe -v + + @echo + @echo "### go module versions:" + @go list -m all + + @echo + @echo "### go version used for building:" + @grep -m 1 go go.mod + + +dir: + rm -rf a + mkdir -p a/b/c + date > a/filea + date > a/b/fileb + date > a/b/c/filec + +bench: all + dd if=/dev/zero of=t/fileZ bs=1024 count=200000 + dd if=/dev/zero of=t/fileM bs=1024 count=200000 + dd if=/dev/zero of=t/fileS bs=1024 count=200000 + dd if=/dev/zero of=t/fileE bs=1024 count=200000 + /usr/bin/time -f "%S" ./gowipe -Z t/fileZ + /usr/bin/time -f "%S" ./gowipe -M t/fileM + /usr/bin/time -f "%S" ./gowipe -S t/fileS + /usr/bin/time -f "%S" ./gowipe -E t/fileE diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c72f28fb6bd1cb747c481cda40701caece45334c GIT binary patch literal 8743 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 Rjlv{oHQqAaVJsq?$UCOU8Xo`v literal 0 HcmV?d00001 diff --git a/crypto.go b/crypto.go new file mode 100644 index 0000000..a977af5 --- /dev/null +++ b/crypto.go @@ -0,0 +1,244 @@ +/* +Copyright © 2022 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package main + +import ( + "crypto/cipher" + cryptorand "crypto/rand" + "errors" + "fmt" + "io" + "math/big" + mathrand "math/rand" + "os" + "time" + "unsafe" + + "golang.org/x/crypto/argon2" + chapo "golang.org/x/crypto/chacha20poly1305" +) + +const ( + SaltSize = 32 // in bytes + NonceSize = 24 // in bytes. taken from aead.NonceSize() + KeySize = uint32(32) // KeySize is 32 bytes (256 bits). + KeyTime = uint32(5) + KeyMemory = uint32(1024 * 64) // KeyMemory in KiB. here, 64 MiB. + KeyThreads = uint8(4) + chunkSize = 1024 * 32 // chunkSize in bytes. here, 32 KiB. + + letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-" + + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letters) { + b[i] = letters[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + + return *(*string)(unsafe.Pointer(&b)) +} + +func GetRandomKey() ([]byte, error) { + password, err := GenerateSecureRandomBytes(int(chapo.KeySize)) + if err != nil { + return nil, err + } + + salt, err := GenerateSecureRandomBytes(chapo.NonceSize) + if err != nil { + return nil, err + } + + key := argon2.IDKey(password, salt, KeyTime, KeyMemory, KeyThreads, chapo.KeySize) + + return key, nil +} + +func Encrypt(c *Conf, filename string) error { + info, err := os.Stat(filename) + if err != nil { + return err + } + + size := info.Size() + + outfile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + return err + } + defer outfile.Close() + + key, err := GetRandomKey() + if err != nil { + return err + } + + aead, err := chapo.NewX(key) + if err != nil { + return err + } + + for i := 0; i < c.count; i++ { + for { + EncryptChunk(aead, outfile, size) + size = size - chunkSize + + if size <= 0 { + break + } + } + } + + return nil +} + +func EncryptChunk(aead cipher.AEAD, file *os.File, size int64) error { + chunk := make([]byte, size) + nonce, err := GenerateSecureRandomBytes(int(chapo.NonceSize)) + if err != nil { + return err + } + + cipher := aead.Seal(nil, nonce, chunk, nil) + + n, err := file.Write(cipher[:size]) + if err != nil { + return err + } + + if int64(n) != size { + return errors.New("invalid number of bytes written") + } + + return nil +} + +/* +func Encrypt(c *Conf, filename string) error { + salt, err := GetRand(KeySize) + if err != nil { + return err + } + + salt1, err := GetRand(KeySize) + if err != nil { + return err + } + + outfile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + return err + } + defer outfile.Close() + + key := argon2.IDKey(salt1, salt, KeyTime, KeyMemory, KeyThreads, KeySize) + + aead, err := chacha20poly1305.NewX(key) + if err != nil { + return err + } + + buf := make([]byte, chunkSize) + ad_counter := 0 // associated data is a counter + + for { + if n > 0 { + // Select a random nonce, and leave capacity for the ciphertext. + nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+n+aead.Overhead()) + if m, err := cryptorand.Read(nonce); err != nil || m != aead.NonceSize() { + return err + } + + msg := buf[:n] + // Encrypt the message and append the ciphertext to the nonce. + encryptedMsg := aead.Seal(nonce, nonce, msg, []byte(string(ad_counter))) + outfile.Write(encryptedMsg) + ad_counter += 1 + } + + if err == io.EOF { + break + } + + if err != nil { + log.Println("Error when reading input file chunk :", err) + panic(err) + } + } +} +*/ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..02fb021 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module gowipe + +go 1.20 + +require ( + github.com/JojiiOfficial/shred v1.2.1 // indirect + github.com/lu4p/shred v0.0.0-20201211173428-0347b645d724 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/sys v0.14.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..049ed97 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/JojiiOfficial/shred v1.2.1 h1:658CFVTqcAkYVg815vW+guYnyJTLOIoS15tMyPTYhNo= +github.com/JojiiOfficial/shred v1.2.1/go.mod h1:/OAxd6eYOhrXb3KW+2wmDog2BiFlUld8oJEKa+xblxU= +github.com/lu4p/shred v0.0.0-20201211173428-0347b645d724 h1:nLJRUakdvy2j7JsefrtAUqGRbfJUKKqRu/3BCRA9mIQ= +github.com/lu4p/shred v0.0.0-20201211173428-0347b645d724/go.mod h1:6b1kEKx7IPBboPSTnoJZE5sbSDjcNkHHO3Hii8TU8XY= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..b9ed8fe --- /dev/null +++ b/main.go @@ -0,0 +1,238 @@ +/* +Copyright © 2022 Thomas von Dein + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "github.com/JojiiOfficial/shred" + flag "github.com/spf13/pflag" +) + +const VERSION string = "0.0.2" +const Usage string = `This is gowipe - destruct files in a non-recoverable way. + +Usage: gowipe [-rcvz] ... + +Options: +-r --recursive Delete recursively +-c --count Overwrite files times +-m --mode Use for overwriting (or use -E, -S, -M, -Z) +-n --nodelete Do not delete files after overwriting +-N --norename Do not rename the files +-v --verbose Verbose output +-V --version Show program version +-h --help Show usage + +Available modes: +zero Overwrite with zeroes (-Z) +math Overwrite with math random bytes (-M) +secure Overwrite with secure random bytes (default) (-S) +encrypt Overwrite with ChaCha2Poly1305 encryption (most secure) (-E)` + +type Conf struct { + mode string + count int + recurse bool + nodelete bool + norename bool + verbose bool +} + +func main() { + showversion := false + showhelp := false + optzero := false + optsecure := false + optmath := false + optencrypt := false + + c := Conf{ + verbose: false, + mode: `secure`, + count: 30, + recurse: false, + nodelete: false, + norename: false, + } + + flag.BoolVarP(&showversion, "version", "V", showversion, "show version") + flag.BoolVarP(&showhelp, "help", "h", showversion, "show help") + flag.BoolVarP(&c.verbose, "verbose", "v", c.verbose, "verbose") + + flag.StringVarP(&c.mode, "mode", "m", c.mode, "overwrite mode") + + flag.BoolVarP(&optzero, "zero", "Z", optzero, "zero mode") + flag.BoolVarP(&optsecure, "secure", "S", optsecure, "secure mode") + flag.BoolVarP(&optmath, "math", "M", optmath, "math mode") + flag.BoolVarP(&optmath, "encrypt", "E", optmath, "encrypt mode") + + flag.BoolVarP(&c.recurse, "recursive", "r", c.recurse, "recursive") + flag.BoolVarP(&c.nodelete, "nodelete", "n", c.nodelete, "don't delete") + flag.BoolVarP(&c.norename, "norename", "N", c.norename, "don't rename") + flag.IntVarP(&c.count, "count", "c", c.count, "overwrite count") + + flag.Parse() + + if showversion { + fmt.Printf("This is gowipe version %s\n", VERSION) + os.Exit(0) + } + + if showhelp { + fmt.Println(Usage) + os.Exit(0) + } + + if len(flag.Args()) == 0 { + fmt.Println(Usage) + os.Exit(0) + } + + var option shred.WriteOptions + + if optzero { + option = shred.WriteZeros + } + if optmath { + option = shred.WriteRand + } + if optsecure { + option = shred.WriteRandSecure + } + if optencrypt { + c.mode = "encrypt" + } + + switch c.mode { + case `secure`: + option = shred.WriteRandSecure + case `math`: + option = shred.WriteRand + case `zero`: + option = shred.WriteZeros + case `encrypt`: + optencrypt = true + default: + option = shred.WriteRandSecure + } + + shredder := shred.Shredder{} + shredconf := shred.NewShredderConf(&shredder, option, c.count, !c.nodelete) + + for _, file := range flag.Args() { + Wipe(file, &c, shredconf) + } +} + +func Wipe(file string, c *Conf, wiper *shred.ShredderConf) { + if info, err := os.Stat(file); err == nil { + + if info.IsDir() { + if !c.recurse { + fmt.Printf("-r not set, ignoring directory %s\n", file) + return + } + + files, err := ioutil.ReadDir(file) + if err != nil { + log.Fatal(err) + } + + for _, entry := range files { + Wipe(filepath.Join(file, entry.Name()), c, wiper) + } + + if !c.nodelete { + err = os.Remove(Rename(file, c)) + if err != nil { + log.Fatal(err) + } + } + } else { + if c.mode == "encrypt" { + err := Encrypt(c, file) + if err != nil { + log.Fatal(err) + } + + Rename(file, c) + } else { + wiper.ShredFile(Rename(file, c)) + } + } + + if c.verbose { + fmt.Printf("Wiped %d times: %s\n", c.count, file) + } + } else { + if os.IsNotExist(err) { + fmt.Printf("No such file or directory: %s\n", file) + } else { + fmt.Println(err) + } + + os.Exit(1) + } +} + +func Rename(file string, c *Conf) string { + var newname string + dir := filepath.Dir(file) + base := filepath.Base(file) + length := len(base) + + for i := 0; i < c.count; i++ { + for { + switch c.mode { + case `secure`: + new, err := GenerateSecureRandomString(length) + if err != nil { + log.Fatal(err) + } + newname = new + case `math`: + newname = GenerateMathRandomString(length) + case `zero`: + newname = strings.Repeat("0", length) + } + if newname != base { + break + } + } + + /* + if c.verbose { + fmt.Printf("renaming %s/%s => %s/%s\n", dir, base, dir, newname) + } + */ + + err := os.Rename(filepath.Join(dir, base), filepath.Join(dir, newname)) + if err != nil { + log.Fatal(err) + } + + base = newname + } + + return filepath.Join(dir, newname) +} diff --git a/mkrel.sh b/mkrel.sh new file mode 100755 index 0000000..894deaf --- /dev/null +++ b/mkrel.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# Copyright © 2022 Thomas von Dein + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +# get list with: go tool dist list +DIST="darwin/amd64 +freebsd/amd64 +linux/amd64 +netbsd/amd64 +openbsd/amd64 +windows/amd64" + +tool="$1" +version="$2" + +if test -z "$version"; then + echo "Usage: $0 " + exit 1 +fi + +rm -rf releases +mkdir -p releases + + +for D in $DIST; do + os=${D/\/*/} + arch=${D/*\//} + binfile="releases/${tool}-${os}-${arch}-${version}" + tardir="${tool}-${os}-${arch}-${version}" + tarfile="releases/${tool}-${os}-${arch}-${version}.tar.gz" + set -x + GOOS=${os} GOARCH=${arch} go build -o ${binfile} -ldflags "-X 'github.com/tlinden/tablizer/cfg.VERSION=${version}'" + mkdir -p ${tardir} + cp ${binfile} README.md LICENSE ${tardir}/ + echo 'tool = tablizer +PREFIX = /usr/local +UID = root +GID = 0 + +install: + install -d -o $(UID) -g $(GID) $(PREFIX)/bin + install -d -o $(UID) -g $(GID) $(PREFIX)/man/man1 + install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/ + install -o $(UID) -g $(GID) -m 444 $(tool).1 $(PREFIX)/man/man1/' > ${tardir}/Makefile + tar cpzf ${tarfile} ${tardir} + sha256sum ${binfile} | cut -d' ' -f1 > ${binfile}.sha256 + sha256sum ${tarfile} | cut -d' ' -f1 > ${tarfile}.sha256 + rm -rf ${tardir} + set +x +done +