mirror of
https://codeberg.org/scip/gowipe.git
synced 2025-12-17 12:40:59 +01:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f7aabc75be | |||
| 88febcf664 | |||
|
|
8dc372a222 |
35
.github/workflows/ci.yaml
vendored
35
.github/workflows/ci.yaml
vendored
@@ -1,35 +0,0 @@
|
|||||||
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
87
.github/workflows/release.yaml
vendored
@@ -1,87 +0,0 @@
|
|||||||
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}}
|
|
||||||
65
.goreleaser.yaml
Normal file
65
.goreleaser.yaml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
- go mod tidy
|
||||||
|
|
||||||
|
gitea_urls:
|
||||||
|
api: https://codeberg.org/api/v1
|
||||||
|
download: https://codeberg.org
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- freebsd
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- formats: [tar.gz]
|
||||||
|
# this name template makes the OS and Arch compatible with the results of `uname`.
|
||||||
|
name_template: >-
|
||||||
|
{{ .ProjectName }}_
|
||||||
|
{{- title .Os }}_
|
||||||
|
{{- if eq .Arch "amd64" }}x86_64
|
||||||
|
{{- else if eq .Arch "386" }}i386
|
||||||
|
{{- else }}{{ .Arch }}{{ end }}
|
||||||
|
{{- if .Arm }}v{{ .Arm }}{{ end }}_{{ .Tag }}
|
||||||
|
# use zip for windows archives
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
formats: [zip]
|
||||||
|
- goos: linux
|
||||||
|
formats: [tar.gz,binary]
|
||||||
|
files:
|
||||||
|
- src: "*.md"
|
||||||
|
strip_parent: true
|
||||||
|
- src: Makefile.dist
|
||||||
|
dst: Makefile
|
||||||
|
wrap_in_directory: true
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- "^docs:"
|
||||||
|
- "^test:"
|
||||||
|
groups:
|
||||||
|
- title: Improved
|
||||||
|
regexp: '^.*?(feat|add|new)(\([[:word:]]+\))??!?:.+$'
|
||||||
|
order: 0
|
||||||
|
- title: Fixed
|
||||||
|
regexp: '^.*?(bug|fix)(\([[:word:]]+\))??!?:.+$'
|
||||||
|
order: 1
|
||||||
|
- title: Changed
|
||||||
|
order: 999
|
||||||
|
|
||||||
|
release:
|
||||||
|
header: "# Release Notes"
|
||||||
|
footer: >-
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Full Changelog: [{{ .PreviousTag }}...{{ .Tag }}](https://codeberg.org/scip/gowipe/compare/{{ .PreviousTag }}...{{ .Tag }})
|
||||||
36
.woodpecker/build.yaml
Normal file
36
.woodpecker/build.yaml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- linux/amd64
|
||||||
|
goversion:
|
||||||
|
- 1.24
|
||||||
|
|
||||||
|
labels:
|
||||||
|
platform: ${platform}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
build:
|
||||||
|
when:
|
||||||
|
event: [push]
|
||||||
|
image: golang:${goversion}
|
||||||
|
commands:
|
||||||
|
- go get
|
||||||
|
- go build
|
||||||
|
|
||||||
|
linter:
|
||||||
|
when:
|
||||||
|
event: [push]
|
||||||
|
image: golang:${goversion}
|
||||||
|
commands:
|
||||||
|
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.5.0
|
||||||
|
- golangci-lint --version
|
||||||
|
- golangci-lint run ./...
|
||||||
|
depends_on: [build]
|
||||||
|
|
||||||
|
test:
|
||||||
|
when:
|
||||||
|
event: [push]
|
||||||
|
image: golang:${goversion}
|
||||||
|
commands:
|
||||||
|
- go get
|
||||||
|
- go test -v -cover
|
||||||
|
depends_on: [build,linter]
|
||||||
15
.woodpecker/release.yaml
Normal file
15
.woodpecker/release.yaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# build release
|
||||||
|
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
goreleaser:
|
||||||
|
image: goreleaser/goreleaser
|
||||||
|
when:
|
||||||
|
event: [tag]
|
||||||
|
environment:
|
||||||
|
GITEA_TOKEN:
|
||||||
|
from_secret: DEPLOY_TOKEN
|
||||||
|
commands:
|
||||||
|
- goreleaser release --clean --verbose
|
||||||
89
Makefile
Normal file
89
Makefile
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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
|
||||||
18
Makefile.dist
Normal file
18
Makefile.dist
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# -*-make-*-
|
||||||
|
|
||||||
|
.PHONY: install all
|
||||||
|
|
||||||
|
tool = rpn
|
||||||
|
PREFIX = /usr/local
|
||||||
|
UID = root
|
||||||
|
GID = 0
|
||||||
|
|
||||||
|
all:
|
||||||
|
@echo "Type 'sudo make install' to install the tool."
|
||||||
|
@echo "To change prefix, type 'sudo make install PREFIX=/opt'"
|
||||||
|
|
||||||
|
install:
|
||||||
|
install -d -o $(UID) -g $(GID) $(PREFIX)/bin
|
||||||
|
install -d -o $(UID) -g $(GID) $(PREFIX)/share/doc
|
||||||
|
install -o $(UID) -g $(GID) -m 555 $(tool) $(PREFIX)/sbin/
|
||||||
|
install -o $(UID) -g $(GID) -m 444 *.md $(PREFIX)/share/doc/
|
||||||
13
README.md
13
README.md
@@ -1,11 +1,8 @@
|
|||||||
[](https://github.com/tlinden/gowipe/blob/master/LICENSE)
|
|
||||||
[](https://goreportcard.com/report/github.com/tlinden/gowipe)
|
|
||||||
[](https://github.com/TLINDEN/gowipe/releases/latest)
|
|
||||||
|
|
||||||
## gowipe - securely delete files and directories (not for SSD)
|
## gowipe - securely delete files and directories (not for SSD)
|
||||||
|
|
||||||
> [!CAUTION]
|
[](https://ci.codeberg.org/repos/15612)
|
||||||
> This software is now being maintained on [Codeberg](https://codeberg.org/scip/gowipe/).
|
[](https://codeberg.org/scip/gowipe/raw/branch/main/LICENSE)
|
||||||
|
[](https://goreportcard.com/report/codeberg.org/scip/gowipe)
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
@@ -81,7 +78,7 @@ that's the best way for me to forget to do something.
|
|||||||
|
|
||||||
In order to report a bug, unexpected behavior, feature requests
|
In order to report a bug, unexpected behavior, feature requests
|
||||||
or to submit a patch, please open an issue on github:
|
or to submit a patch, please open an issue on github:
|
||||||
https://github.com/TLINDEN/gowipe/issues.
|
https://codeberg.org/scip/gowipe/issues.
|
||||||
|
|
||||||
## Copyright and license
|
## Copyright and license
|
||||||
|
|
||||||
@@ -93,4 +90,4 @@ T.v.Dein <tom AT vondein DOT org>
|
|||||||
|
|
||||||
## Project homepage
|
## Project homepage
|
||||||
|
|
||||||
https://github.com/TLINDEN/gowipe
|
https://codeberg.org/scip/gowipe
|
||||||
|
|||||||
205
crypto.go
Normal file
205
crypto.go
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
/*
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
cryptorand "crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"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<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||||
|
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||||
|
)
|
||||||
|
|
||||||
|
// via https://gist.github.com/dopey/c69559607800d2f2f90b1b1ed4e550fb
|
||||||
|
func AssertAvailablePRNG() {
|
||||||
|
// Assert that a cryptographically secure PRNG is available.
|
||||||
|
// Panic otherwise.
|
||||||
|
buf := make([]byte, 1)
|
||||||
|
|
||||||
|
_, err := io.ReadFull(cryptorand.Reader, buf)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("crypto/rand is unavailable: Read() failed with %#v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateRandomBytes returns securely generated random bytes.
|
||||||
|
// It will return an error if the system's secure random
|
||||||
|
// number generator fails to function correctly, in which
|
||||||
|
// case the caller should not continue.
|
||||||
|
func GenerateSecureRandomBytes(n int) ([]byte, error) {
|
||||||
|
b := make([]byte, n)
|
||||||
|
_, err := cryptorand.Read(b)
|
||||||
|
// Note that err == nil only if we read len(b) bytes.
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateRandomString returns a securely generated random string.
|
||||||
|
// It will return an error if the system's secure random
|
||||||
|
// number generator fails to function correctly, in which
|
||||||
|
// case the caller should not continue.
|
||||||
|
func GenerateSecureRandomString(n int) (string, error) {
|
||||||
|
ret := make([]byte, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
num, err := cryptorand.Int(cryptorand.Reader, big.NewInt(int64(len(letters))))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ret[i] = letters[num.Int64()]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// via:
|
||||||
|
// https://stackoverflow.com/a/31832326
|
||||||
|
func GenerateMathRandomString(n int) string {
|
||||||
|
b := make([]byte, n)
|
||||||
|
var src = mathrand.NewSource(time.Now().UnixNano())
|
||||||
|
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
|
||||||
|
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 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 func() {
|
||||||
|
if err := outfile.Close(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
14
go.mod
Normal file
14
go.mod
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
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
|
||||||
|
)
|
||||||
10
go.sum
Normal file
10
go.sum
Normal file
@@ -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=
|
||||||
261
main.go
Normal file
261
main.go
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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] <file|directory>...
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-r --recursive Delete <dir> recursively
|
||||||
|
-c --count <num> Overwrite files <num> times
|
||||||
|
-m --mode <mode> Use <mode> 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)
|
||||||
|
}
|
||||||
65
mkrel.sh
Executable file
65
mkrel.sh
Executable file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
# 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 <tool name> <release version>"
|
||||||
|
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 'codeberg.org/scip/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
|
||||||
|
|
||||||
Reference in New Issue
Block a user