13 Commits

Author SHA1 Message Date
a90d6e5c3c fix algo 2025-02-05 18:03:52 +01:00
ed4ad2340b add changelog builder, update release builder 2025-02-05 17:56:05 +01:00
f7b0cfa905 fix heading 2025-01-19 16:27:03 +01:00
b914fdfcdf simplified verbose output and enhanced readme. 2025-01-19 16:23:16 +01:00
f2116f39ef fix name typo 2025-01-18 11:36:56 +01:00
5907c5b2be add badges 2025-01-18 11:35:12 +01:00
8dbdebee46 fix ci builder 2025-01-18 11:33:02 +01:00
daabdc5c9b update dependencies 2025-01-18 11:29:45 +01:00
f4b1ba5863 fixes:
- fix encryption, used the wrong nonce size
- encrypted files were not deleted
- fixed recursion
- fixed linter warnings
2025-01-18 11:27:57 +01:00
eaca5ad181 fix typo 2025-01-18 11:03:18 +01:00
5ca1be594b mv to correct dir 2025-01-18 11:02:26 +01:00
aa03ba5281 add ci pipelines 2025-01-18 11:00:15 +01:00
74c801b914 typo 2023-11-25 14:53:07 +01:00
8 changed files with 237 additions and 86 deletions

35
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: build-and-test-gowipe
on: [push, pull_request]
jobs:
build:
strategy:
matrix:
version: ['1.21','1.22']
os: [ubuntu-latest, macos-latest, windows-latest]
name: Build
runs-on: ${{ matrix.os }}
steps:
- name: Set up Go ${{ matrix.version }}
uses: actions/setup-go@v5
with:
go-version: '${{ matrix.version }}'
id: go
- name: checkout
uses: actions/checkout@v4
- name: build
run: go build
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v5
with:
go-version: 1.22
- uses: actions/checkout@v4
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
skip-cache: true

87
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,87 @@
name: build-release
on:
push:
tags:
- "v*.*.*"
jobs:
release:
name: Build Release Assets
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.22.11
- name: Build the executables
run: ./mkrel.sh gowipe ${{ github.ref_name}}
- name: List the executables
run: ls -l ./releases
- name: Upload the binaries
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref_name }}
file: ./releases/*
file_glob: true
- name: Build Changelog
id: github_release
uses: mikepenz/release-changelog-builder-action@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
mode: "PR"
configurationJson: |
{
"template": "#{{CHANGELOG}}\n\n**Full Changelog**: #{{RELEASE_DIFF}}",
"pr_template": "- #{{TITLE}} (##{{NUMBER}}) by #{{AUTHOR}}\n#{{BODY}}",
"empty_template": "- no changes",
"categories": [
{
"title": "## New Features",
"labels": ["add", "feature"]
},
{
"title": "## Bug Fixes",
"labels": ["fix", "bug", "revert"]
},
{
"title": "## Documentation Enhancements",
"labels": ["doc"]
},
{
"title": "## Refactoring Efforts",
"labels": ["refactor"]
},
{
"title": "## Miscellaneus Changes",
"labels": []
}
],
"ignore_labels": [
"duplicate", "good first issue", "help wanted", "invalid", "question", "wontfix"
],
"label_extractor": [
{
"pattern": "(.) (.+)",
"target": "$1"
},
{
"pattern": "(.) (.+)",
"target": "$1",
"on_property": "title"
}
]
}
- name: Create Release
uses: softprops/action-gh-release@v2
with:
body: ${{steps.github_release.outputs.changelog}}

View File

@@ -55,8 +55,8 @@ goupdate:
buildall:
./mkrel.sh $(tool) $(VERSION)
release: buildall
gh release create v$(VERSION) --generate-notes releases/*
release:
gh release create v$(VERSION) --generate-notes
show-versions: buildlocal
@echo "### gowipe version:"

View File

@@ -1,5 +1,57 @@
## 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)
## Description
`gowipe` is a simple self contained tool to securely wipe files and
directories. By default it renames and overwrites files and
directories 30 times and uses the `secure` mode, which uses strong
random bytes for the overwriting process. Gowipe writes as much bytes
into a file as its original size.
You can tweak mode and round numbers. Other modes are `zero`, which
uses zeroes for overwriting (not recommended) or `encrypt` which
encrypts the data using ChaCha20Poly1305 and a strong random key. This is the most
secure but also to slowest mode.
Although you can use `gowipe` on SSD disks, it doesn't make much
sense. To wipe such a disk you have to resort to other means. But you
can savely use it on magnetic discs or usb drives.
Of course there are many other such tools available, this one is
insofar special as you can download a pre-compiled binary without any
library dependencies. This allows you to wipe files on systems, where
you cannot install a wiper via some package management (such as
appliance systems or vm's).
## Example
Overwrite the directory `vhs` recursively 50 times using strong
encryption and verbose output:
```shell
gowipe -c 50 -E -r -v vhs
Wiped vhs/help.png (355011 bytes)
Wiped vhs/rec.Dockerfile (348 bytes)
Wiped vhs/rec.gif (3533338 bytes)
Wiped vhs/rec.tape (852 bytes)
Wiped vhs (4096 bytes)
Dirs wiped: 1
Files wiped: 5
Bytes deleted: 3889549
Time elapsed: 426.286639ms
Overwritten: 50 times
Wipe mode: encrypt
Recurse dirs: true
```
## Usage
```
Usage: gowipe [-rcvz] <file|directory>...
@@ -22,7 +74,7 @@ encrypt Overwrite with ChaCha2Poly1305 encryption (most secure) (-E)
## Getting help
Although I'm happy to hear from tablizer users in private email,
Although I'm happy to hear from gowipe users in private email,
that's the best way for me to forget to do something.
In order to report a bug, unexpected behavior, feature requests

View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022 Thomas von Dein
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
@@ -119,7 +119,7 @@ func GetRandomKey() ([]byte, error) {
return nil, err
}
salt, err := GenerateSecureRandomBytes(chapo.NonceSize)
salt, err := GenerateSecureRandomBytes(chapo.NonceSizeX)
if err != nil {
return nil, err
}
@@ -156,11 +156,17 @@ func Encrypt(c *Conf, filename string) error {
for i := 0; i < c.count; i++ {
for {
if size < chunkSize {
EncryptChunk(aead, outfile, size)
if err := EncryptChunk(aead, outfile, size); err != nil {
return err
}
break
}
EncryptChunk(aead, outfile, chunkSize)
if err := EncryptChunk(aead, outfile, chunkSize); err != nil {
return err
}
size = size - chunkSize
if size <= 0 {
@@ -174,7 +180,7 @@ func Encrypt(c *Conf, filename string) error {
func EncryptChunk(aead cipher.AEAD, file *os.File, size int64) error {
chunk := make([]byte, size)
nonce, err := GenerateSecureRandomBytes(int(chapo.NonceSize))
nonce, err := GenerateSecureRandomBytes(int(chapo.NonceSizeX))
if err != nil {
return err
}
@@ -192,58 +198,3 @@ func EncryptChunk(aead cipher.AEAD, file *os.File, size int64) error {
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)
}
}
}
*/

9
go.mod
View File

@@ -3,9 +3,12 @@ module gowipe
go 1.20
require (
github.com/JojiiOfficial/shred v1.2.1 // indirect
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
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/sys v0.14.0 // indirect
)

65
main.go
View File

@@ -1,5 +1,5 @@
/*
Copyright © 2022 Thomas von Dein
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
@@ -18,17 +18,17 @@ package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/JojiiOfficial/shred"
flag "github.com/spf13/pflag"
)
const VERSION string = "0.0.2"
const VERSION string = "0.0.4"
const Usage string = `This is gowipe - destruct files in a non-recoverable way.
Usage: gowipe [-rcvz] <file|directory>...
@@ -56,6 +56,9 @@ type Conf struct {
nodelete bool
norename bool
verbose bool
files int
dirs int
size int64
}
func main() {
@@ -84,7 +87,7 @@ func main() {
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(&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")
@@ -139,21 +142,33 @@ func main() {
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 := ioutil.ReadDir(file)
files, err := os.ReadDir(file)
if err != nil {
log.Fatal(err)
}
@@ -162,6 +177,22 @@ func Wipe(file string, c *Conf, wiper *shred.ShredderConf) {
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 {
@@ -169,21 +200,19 @@ func Wipe(file string, c *Conf, wiper *shred.ShredderConf) {
}
}
} else {
if c.mode == "encrypt" {
err := Encrypt(c, file)
if err != nil {
if err := wiper.ShredFile(Rename(file, c)); err != nil {
log.Fatal(err)
}
Rename(file, c)
} else {
wiper.ShredFile(Rename(file, c))
}
c.size += info.Size()
}
if c.verbose {
fmt.Printf("Wiped %d times: %s\n", c.count, file)
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)
@@ -204,7 +233,7 @@ func Rename(file string, c *Conf) string {
for i := 0; i < c.count; i++ {
for {
switch c.mode {
case `secure`:
case `secure`, `encrypt`:
new, err := GenerateSecureRandomString(length)
if err != nil {
log.Fatal(err)
@@ -220,12 +249,6 @@ func Rename(file string, c *Conf) string {
}
}
/*
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)

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# Copyright © 2022 Thomas von Dein
# 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
@@ -46,7 +46,7 @@ for D in $DIST; do
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
echo 'tool = gowipe
PREFIX = /usr/local
UID = root
GID = 0