diff --git a/Makefile b/Makefile deleted file mode 100644 index df4778b..0000000 --- a/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -# 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: - gh release create v$(VERSION) --generate-notes - -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 index e3058de..b091cf8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -## gowipe - securely delete files and directories (not for SSD) - -[![Actions](https://github.com/tlinden/gowipe/actions/workflows/ci.yaml/badge.svg)](https://github.com/tlinden/gowipe/actions) [![License](https://img.shields.io/badge/license-GPL-blue.svg)](https://github.com/tlinden/gowipe/blob/master/LICENSE) [![Go Report Card](https://goreportcard.com/badge/github.com/tlinden/gowipe)](https://goreportcard.com/report/github.com/tlinden/gowipe) [![GitHub release](https://img.shields.io/github/v/release/tlinden/gowipe?color=%2300a719)](https://github.com/TLINDEN/gowipe/releases/latest) +## gowipe - securely delete files and directories (not for SSD) + +> [!CAUTION] +> This software is now being maintained on [Codeberg](https://codeberg.org/scip/gowipe/). + ## Description `gowipe` is a simple self contained tool to securely wipe files and diff --git a/crypto.go b/crypto.go deleted file mode 100644 index ea65d92..0000000 --- a/crypto.go +++ /dev/null @@ -1,200 +0,0 @@ -/* -Copyright © 2022-2025 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.NonceSizeX) - 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 { - if size < chunkSize { - if err := EncryptChunk(aead, outfile, size); err != nil { - return err - } - - break - } - - if err := EncryptChunk(aead, outfile, chunkSize); err != nil { - return err - } - - 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.NonceSizeX)) - 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 -} diff --git a/go.mod b/go.mod deleted file mode 100644 index 2ff63bc..0000000 --- a/go.mod +++ /dev/null @@ -1,14 +0,0 @@ -module gowipe - -go 1.20 - -require ( - github.com/JojiiOfficial/shred v1.2.1 - github.com/spf13/pflag v1.0.5 - golang.org/x/crypto v0.15.0 -) - -require ( - github.com/lu4p/shred v0.0.0-20201211173428-0347b645d724 // indirect - golang.org/x/sys v0.14.0 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 049ed97..0000000 --- a/go.sum +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index 54af476..0000000 --- a/main.go +++ /dev/null @@ -1,261 +0,0 @@ -/* -Copyright © 2022-2025 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" - "log" - "os" - "path/filepath" - "strings" - "time" - - "github.com/JojiiOfficial/shred" - flag "github.com/spf13/pflag" -) - -const VERSION string = "0.0.4" -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 - files int - dirs int - size int64 -} - -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(&optencrypt, "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) - - start := time.Now() - - for _, file := range flag.Args() { - Wipe(file, &c, shredconf) - } - - if c.verbose { - fmt.Println() - fmt.Printf(" Dirs wiped: %d\n", c.dirs) - fmt.Printf(" Files wiped: %d\n", c.files) - fmt.Printf("Bytes deleted: %d\n", c.size) - fmt.Printf(" Time elapsed: %s\n", time.Since(start)) - fmt.Printf(" Overwritten: %d times\n", c.count) - fmt.Printf(" Wipe mode: %s\n", c.mode) - fmt.Printf(" Recurse dirs: %t\n", c.recurse) - } -} - -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 := os.ReadDir(file) - if err != nil { - log.Fatal(err) - } - - for _, entry := range files { - Wipe(filepath.Join(file, entry.Name()), c, wiper) - } - - // delete dir - if !c.nodelete { - err = os.Remove(Rename(file, c)) - if err != nil { - log.Fatal(err) - } - - c.dirs++ - } - } else { - if c.mode == "encrypt" { - if err := Encrypt(c, file); err != nil { - log.Fatal(err) - } - - // delete encrypted file - if !c.nodelete { - err = os.Remove(Rename(file, c)) - if err != nil { - log.Fatal(err) - } - } - } else { - if err := wiper.ShredFile(Rename(file, c)); err != nil { - log.Fatal(err) - } - } - - c.size += info.Size() - } - - if c.verbose { - fmt.Printf("Wiped %s (%d bytes)\n", file, info.Size()) - } - - c.files++ - } 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`, `encrypt`: - 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 - } - } - - 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 deleted file mode 100755 index f0f3d2c..0000000 --- a/mkrel.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -# Copyright © 2022-2025 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 = gowipe -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 -