mirror of
https://github.com/martijnvanbrummelen/nwipe.git
synced 2026-02-21 22:42:29 +00:00
Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3bf91223b9 | ||
|
|
7993947977 | ||
|
|
2e24184122 | ||
|
|
0fe2a32ec4 | ||
|
|
9ffa4513a6 | ||
|
|
26ece7a052 | ||
|
|
e964dbab09 | ||
|
|
7df282386e | ||
|
|
951bb5642d | ||
|
|
02f5d3273a | ||
|
|
75378ca87f | ||
|
|
c8860875b3 | ||
|
|
096a201a66 | ||
|
|
ef04909ffe | ||
|
|
94548ad739 | ||
|
|
3c15a420e3 | ||
|
|
c323e73984 | ||
|
|
f6840105b0 | ||
|
|
bd1e8af845 | ||
|
|
07f59467a1 | ||
|
|
a3727d7d21 | ||
|
|
9c91d19d14 | ||
|
|
fea3d8c303 | ||
|
|
f8f64ba7e7 | ||
|
|
dcdca64db7 | ||
|
|
394ba386e5 | ||
|
|
8e291d5bbb | ||
|
|
76f928c73d | ||
|
|
ee3441f32c | ||
|
|
1aaae91e88 | ||
|
|
c50019abc3 | ||
|
|
b8f567bcfe | ||
|
|
65ff9ff64c | ||
|
|
4defd2235a | ||
|
|
9f6f465230 | ||
|
|
1f68f35dc5 | ||
|
|
c235349288 | ||
|
|
26e5cb9894 | ||
|
|
3b6b6e0040 | ||
|
|
fc308b3f34 | ||
|
|
6eae95b995 | ||
|
|
047de16d96 | ||
|
|
57e337537d | ||
|
|
f28bb3d3d6 | ||
|
|
bc2643c72a | ||
|
|
b79530c292 | ||
|
|
5f47df370e | ||
|
|
9a70d23e4a | ||
|
|
259ee26273 | ||
|
|
121624478f | ||
|
|
c13f2477b1 | ||
|
|
d97c8a1014 | ||
|
|
dcfa8f4ea2 | ||
|
|
ccf8eed4ed | ||
|
|
375f8b3f87 | ||
|
|
c51a8e9846 | ||
|
|
066d62352f | ||
|
|
a0339c3bb1 | ||
|
|
0801ca7ae8 | ||
|
|
6792a969dc | ||
|
|
4150dddd84 | ||
|
|
2dbdaf447c | ||
|
|
96127687cd | ||
|
|
24dcbaa40c | ||
|
|
0d043a3745 | ||
|
|
3e79c46f45 | ||
|
|
ae3a8a51d1 | ||
|
|
67df917378 | ||
|
|
04b79bc746 | ||
|
|
f83f229a6a | ||
|
|
30015d1be4 | ||
|
|
071487e4bc | ||
|
|
7fdf6b379a | ||
|
|
0e78efeb40 | ||
|
|
e8c07bddc5 | ||
|
|
b1dfea30d6 | ||
|
|
86cf634ab6 | ||
|
|
1a27e0ac7e | ||
|
|
ed74492a1d | ||
|
|
b8f9307256 | ||
|
|
d9ff3e8f8e | ||
|
|
76c7820002 | ||
|
|
bc1bc190b5 | ||
|
|
8fc559774d | ||
|
|
f48fac0e5b | ||
|
|
a76046ed37 | ||
|
|
ae6c839e3a | ||
|
|
34e42b3c5e | ||
|
|
2f2c0a5153 | ||
|
|
342fb03c1d | ||
|
|
8bc22175ac | ||
|
|
39ee8cfc91 | ||
|
|
316b707308 | ||
|
|
ae6cd21019 | ||
|
|
a01ec958e4 | ||
|
|
ad25e08997 | ||
|
|
59fbac30a8 | ||
|
|
436aa12227 | ||
|
|
628e514058 | ||
|
|
5af773eaac | ||
|
|
16d9885577 | ||
|
|
51e3caeb18 | ||
|
|
764235fc7d | ||
|
|
f594d677a7 | ||
|
|
536ead8f2b | ||
|
|
c29a17d090 | ||
|
|
d630e3bd3c | ||
|
|
997a1867cf | ||
|
|
ddb0329f03 | ||
|
|
b21860d336 |
34
CHANGELOG.md
34
CHANGELOG.md
@@ -1,5 +1,39 @@
|
||||
RELEASE NOTES
|
||||
=============
|
||||
v0.40
|
||||
-----------------------
|
||||
includes the following changes:
|
||||
|
||||
## New Features and improved performance in V0.40
|
||||
* Upon initialization, nwipe identifies and sets the most efficient Pseudo-Random Number Generator (PRNG) as the default, based on a real-time hardware performance test. Thanks to @knogle #698 #699 #700 #706
|
||||
* AES-256-CTR PRNG that utilizes native [AES-NI instructions](https://en.wikipedia.org/wiki/AES_instruction_set) to significantly boost data-wiping throughput on compatible CPUs. Thanks @knogle #695 #660 #659
|
||||
* Implemented a new wipe method, the BMB21-2019 Chinese State Secrets Bureau standard Technical Requirement for Data Sanitization. Thanks @xicaixiaokeke #668
|
||||
* Implemented Direct I/O with large buffers as the default wipe method, reducing write instructions and boosting throughput. This change cut wipe completion times by 5–10% in tests and significantly lowered CPU load—especially on high-density servers wiping 10+ drives simultaneously. Kernel cached I/O remains available via command-line switches. Thanks @knogle #683
|
||||
* Added support for host UUID, serial number, and custom tags in PDF report headers. These options can be configured and toggled in the nwipe config menu ('c'), accessible from the nwipe drive selection screen and are saved to nwipe's config file, /etc/nwipe/nwipe.conf for persistent configuration between restarts." Thanks @PartialVolume #709
|
||||
* Added a new GUI “device topology” view, opened with the 't' key from the device selection screen. Useful if you want to check which device controller, which HBA, etc. your drive is attached. This improves safety and usability on systems with multiple controllers by clearly showing the physical attachment path of each drive without changing wipe behaviour. Thanks @Knogle #697
|
||||
* Enhanced the --exclude option to match devices by their underlying block device ID (major/minor), allowing persistent identifiers in /dev/disk/by-id/ and /dev/disk/by-path/ to be used safely. Legacy string-based matching is preserved. Thanks @Knogle #686
|
||||
* The internal PRNGs are now seeded via getrandom(2) rather than through a long-lived file descriptor to /dev/urandom, to improve reliability. Thanks @Knogle #680
|
||||
|
||||
## Resolved issues
|
||||
* fixed: To resolve issues with partition wipes, as opposed to whole disc wipes, nwipe now includes the device name in the PDF report filename. Previously, when wiping specific partitions (e.g., sudo nwipe /dev/sdc3 /dev/sd4 /dev/sd5) via the CLI, the absence of a disk model or serial number caused PDF filenames to conflict and overwrite each other. This update ensures unique filenames and prevents data loss when processing multiple partitions. Thanks @PartialVolume #676
|
||||
* Add static linking libraries that are required by parted 3.6 @deamen #655
|
||||
* fixed: some declaration changes to satisfy gcc 15 Thanks @Knogle & @xandris #663 #658
|
||||
* Optimised that header code that generates the PDF reports. Thanks @PartialVolume #678
|
||||
* fixed: respect no-blanking methods in --nogui mode. This fixes an issue where a default blanking pass was added to methods which do not support it when in --nogui mode. Existing GUI code overriding the option is never called in --nogui mode, Thanks @desertwitch #682
|
||||
* fixed: sanitize device serial numbers and fix uninitialized/fallback handling. Thanks @Knogle #690
|
||||
* fixed: Automatically create PDF report directory if missing and improve permission model Thanks @Knogle #693
|
||||
* optimise: Validate autonuke and nogui for valid values and harden logic by not using assumptions in if statements for binary values and switching to case statements for value specific checks. Thanks @PartialVolume #701
|
||||
* fixed: Improve str_truncate to fix memory error detected in valgrind #702
|
||||
* fixed: Require strict input of long form options, i.e no abbreviations allowed!. Command line option syntax as specifically described in `nwipe --help` and nwipe man page are only allowed. Corrects a 'feature' in GNU getopt_long(). Thanks @PartialVolume #705
|
||||
|
||||
v0.39
|
||||
-----------------------
|
||||
includes the following changes:
|
||||
- Fix model name Endian for Sandisk-SunDisk drives by @PartialVolume in https://github.com/martijnvanbrummelen/nwipe/pull/643
|
||||
- Implement Bruce Schneier 7-Pass wiping method by @Knogle in https://github.com/martijnvanbrummelen/nwipe/pull/648
|
||||
- Some cleanup in options.c, added missing xoroshiro256_prng argument i… by @Knogle in https://github.com/martijnvanbrummelen/nwipe/pull/651
|
||||
- Removed EXPERIMENTAL! comments for ALFG and Xoroshiro. [#652](https://github.com/martijnvanbrummelen/nwipe/pull/652) Thanks @Knogle
|
||||
- Fix: some declaration changes to satisfy gcc 15. [#663](https://github.com/martijnvanbrummelen/nwipe/pull/663) Thanks @Knogle
|
||||
|
||||
v0.38
|
||||
-----------------------
|
||||
|
||||
511
README.md
511
README.md
@@ -1,86 +1,231 @@
|
||||
# nwipe
|
||||
|
||||

|
||||
[](https://github.com/martijnvanbrummelen/nwipe/releases/)
|
||||
|
||||
nwipe is a fork of the dwipe command originally used by Darik's Boot and Nuke (DBAN). nwipe was created out of a need to run the DBAN dwipe command outside of DBAN, in order to allow its use with any host distribution, thus giving better hardware support.
|
||||
`nwipe` is a fork of the `dwipe` command originally used by Darik's Boot and Nuke (DBAN).
|
||||
It was created to run the DBAN erase engine on any Linux distribution, with better and more modern hardware support.
|
||||
|
||||
nwipe is a program that will securely erase the entire contents of disks. It can wipe a single drive or multiple disks simultaneously. It can operate as both a command line tool without a GUI or with a ncurses GUI as shown in the example below:
|
||||
`nwipe` securely erases the entire contents of block devices. It can wipe a single drive or multiple disks in parallel, either:
|
||||
|
||||
> **Warning**
|
||||
> For some of nwipes features such as smart data in the PDF certificate, HPA/DCO detection and other uses, nwipe utilises smartmontools and hdparm. Therefore both hdparm & smartmontools are a mandatory requirement if you want all of nwipes features to be fully available. If you do not install smartmontools and hdparm, nwipe will provide a warning in the log that these programs cannot be found but will still run but many important features may not work as they should do.
|
||||
- as a **command-line tool** without a GUI, or
|
||||
- with an **ncurses-based GUI**, as shown below:
|
||||
|
||||

|
||||
> **Warning**
|
||||
> For some of nwipe’s features such as SMART data in the PDF certificate, HPA/DCO detection and other functions, nwipe uses external tools: **smartmontools** and **hdparm**.
|
||||
> Both `hdparm` and `smartmontools` are **mandatory** if you want all nwipe features to be fully available.
|
||||
> If they are not installed, nwipe will log a warning and continue, but many important features will not work as intended.
|
||||
|
||||
<i>The video above shows six drives being simultaneously erased. It skips to the completion of all six wipes and shows five drives that were successfully erased and one drive that failed due to an I/O error. The drive that failed would then normally be physically destroyed. The five drives that were successfully wiped with zero errors or failures can then be redeployed.</i>
|
||||

|
||||
|
||||
*The animation above shows six drives being erased in parallel. The view jumps to the completion of all six wipes and shows five drives successfully wiped and one that failed due to an I/O error. The failing drive would typically be physically destroyed. Drives that complete with zero errors can be safely redeployed.*
|
||||
|
||||

|
||||
<i>The snapshot above shows nwipe's three page PDF certificate, drive identifiable information such as serial numbers have been anonymised using the nwipe command line option -q</i>
|
||||
|
||||
*The screenshot above shows nwipe’s three–page PDF certificate. Drive-identifying data such as serial numbers has been anonymised using the `-q` / `--quiet` option.*
|
||||
|
||||
---
|
||||
|
||||
## New in v0.40 (upcoming)
|
||||
|
||||
The upcoming **v0.40** release introduces several major improvements:
|
||||
|
||||
- **AES-256-CTR PRNG**
|
||||
High–performance, cryptographically secure stream generator (AES-NI accelerated where available).
|
||||
- **Large, aligned I/O buffers**
|
||||
Significantly fewer syscalls and better throughput, especially on fast SSDs and NVMe.
|
||||
- **Configurable I/O modes**
|
||||
- `--io-mode=auto` (default): try O_DIRECT, fall back to cached I/O if not supported
|
||||
- `--directio` / `--io-mode=direct`: force direct I/O (O_DIRECT), no fallback
|
||||
- `--cachedio` / `--io-mode=cached`: force kernel cached I/O, never attempt O_DIRECT
|
||||
- **Improved sync behaviour for cached I/O**
|
||||
Sync intervals are again based on a predictable number of bytes written, ensuring timely detection of disk / USB errors without excessive overhead.
|
||||
- **Enhanced device exclusion**
|
||||
`--exclude` now works cleanly with paths like `/dev/disk/by-id/*`, making it easier to exclude specific drives by stable IDs.
|
||||
- **Stronger seeding with `getrandom()`**
|
||||
nwipe now uses the Linux `getrandom()` syscall for PRNG seeding and no longer depends on `/dev/urandom`.
|
||||
- **New BMB21-2019 erase method**
|
||||
Implements the Chinese State Secrets Bureau BMB21-2019 technical requirement for data sanitisation.
|
||||
|
||||
---
|
||||
|
||||
## Erasure methods
|
||||
The user can select from a variety of recognised secure erase methods which include:
|
||||
|
||||
* Fill With Zeros - Fills the device with zeros (0x00), one round only.
|
||||
* Fill With Ones - Fills the device with ones (0xFF), one round only.
|
||||
* RCMP TSSIT OPS-II - Royal Candian Mounted Police Technical Security Standard, OPS-II
|
||||
* DoD Short - The American Department of Defense 5220.22-M short 3 pass wipe (passes 1, 2 & 7).
|
||||
* DoD 5220.22M - The American Department of Defense 5220.22-M full 7 pass wipe.
|
||||
* Gutmann Wipe - Peter Gutmann's method (Secure Deletion of Data from Magnetic and Solid-State Memory).
|
||||
* PRNG Stream - Fills the device with a stream from the PRNG.
|
||||
* Verify Zeros - This method only reads the device and checks that it is filled with zeros (0x00).
|
||||
* Verify Ones - This method only reads the device and checks that it is filled with ones (0xFF).
|
||||
* HMG IS5 enhanced - Secure Sanitisation of Protectively Marked Information or Sensitive Information
|
||||
The user can select from a variety of recognised secure erase methods, including:
|
||||
|
||||
nwipe also includes the following pseudo random number generators (prng):
|
||||
* Mersenne Twister
|
||||
* ISAAC
|
||||
- **Fill With Zeros**
|
||||
Fills the device with zeros (`0x00`).
|
||||
|
||||
In addition to the above, the following prngs will be available in v0.37
|
||||
* XORoshiro-256
|
||||
* Lagged Fibonacci
|
||||
* AES-CTR (openssl)
|
||||
- **Fill With Ones**
|
||||
Fills the device with ones (`0xFF`).
|
||||
|
||||
These can be used to overwrite a drive with a stream of randomly generated characters.
|
||||
- **RCMP TSSIT OPS-II**
|
||||
Royal Canadian Mounted Police Technical Security Standard, OPS-II.
|
||||
|
||||
nwipe can be found in many [Linux distro repositories](#which-linux-distro-uses-the-latest-nwipe).
|
||||
- **DoD Short**
|
||||
U.S. Department of Defense 5220.22-M **short** 3-pass wipe
|
||||
(passes 1, 2 & 7 from the full specification).
|
||||
|
||||
nwipe is also included in [ShredOS](https://github.com/PartialVolume/shredos.x86_64) which was developed in particular to showcase nwipe as a fast-to-boot standalone method similar to DBAN. ShredOS always contains the latest nwipe version.
|
||||
- **DoD 5220.22-M (Full)**
|
||||
Full 7-pass U.S. DoD 5220.22-M wipe.
|
||||
|
||||
## Limitations regarding solid state drives
|
||||
In the current form nwipe does not sanitize solid state drives (hereinafter referred to as SSDs)
|
||||
of any form (SAS / Sata / NVME) and / or form factor (2.5" / 3.5" / PCI) fully due to their nature:
|
||||
SSDs, as the transistors contained in the memory modules are subject to wear, contain in most cases
|
||||
additional memory modules installed as failover for broken sectors outside
|
||||
of the host accessible space (frequently referred to as "overprovisioning") and for garbage collection.
|
||||
Some manufacturers reserve access to these areas only to disk's own controller and firmware.
|
||||
It is therefor always advised to use nwipe / shredOS in conjunction with the manufacturer's or hardware vendor's own tools for sanitization to assure
|
||||
full destruction of the information contained on the disk.
|
||||
Given that most vendors and manufacturers do not provide open source tools, it is advised to validate the outcome by comparing the data on the disk before and after sanitization.
|
||||
A list of the most common tools and instructions for SSD wipes can be found in the [SSD Guide](ssd-guide.md).
|
||||
- **Gutmann Wipe**
|
||||
Peter Gutmann's 35-pass method (“Secure Deletion of Data from Magnetic and Solid-State Memory”).
|
||||
|
||||
## Compiling & Installing
|
||||
- **PRNG Stream**
|
||||
Fills the device with a stream generated by the selected PRNG engine.
|
||||
|
||||
For a development setup, see the [Hacking section](#hacking) below. For a bootable version of the very latest nwipe master that you can write to an USB flash drive or CD/DVD, see the [Quick and easy bootable version of nwipe master section](#quick--easy-usb-bootable-version-of-nwipe-master-for-x86_64-systems) below.
|
||||
- **Verify Zeros**
|
||||
Reads the device and verifies it is filled with zeros (`0x00`).
|
||||
|
||||
`nwipe` requires the following libraries to be installed:
|
||||
- **Verify Ones**
|
||||
Reads the device and verifies it is filled with ones (`0xFF`).
|
||||
|
||||
* ncurses
|
||||
* pthreads
|
||||
* parted
|
||||
* libconfig
|
||||
- **HMG IS5 Enhanced**
|
||||
UK HMG IS5 (Enhanced) sanitisation method for protectively marked or sensitive information.
|
||||
|
||||
`nwipe` also requires the following program to be installed, it will abort with a warning if not found:
|
||||
- **Schneier Wipe**
|
||||
Bruce Schneier's 7-pass mixed–pattern algorithm.
|
||||
|
||||
* hdparm (as of current master and v0.35+)
|
||||
- **BMB21-2019** *(new in v0.40)*
|
||||
Chinese State Secrets Bureau BMB21-2019 technical requirement for data sanitisation.
|
||||
This method overwrites the device with ones (`0xFF`), then zeros (`0x00`), followed by three passes of PRNG-generated random data, and finishes with a final pass of ones (`0xFF`).
|
||||
|
||||
and optionally, but recommended, the following programs:
|
||||
---
|
||||
|
||||
* dmidecode
|
||||
* readlink
|
||||
* smartmontools
|
||||
## PRNG engines
|
||||
|
||||
nwipe includes multiple pseudorandom number generators (PRNGs) for methods that require random data:
|
||||
|
||||
- **AES-256-CTR** *(new in v0.40)*
|
||||
Cryptographically secure, high–throughput counter-mode stream cipher, using hardware AES-NI where available.
|
||||
|
||||
- **XORoshiro-256**
|
||||
Very fast, high–quality non-cryptographic generator, suitable for high–volume random wiping where a CSPRNG is not strictly required.
|
||||
|
||||
- **Mersenne Twister**
|
||||
Well-known high–period PRNG.
|
||||
|
||||
- **ISAAC / ISAAC-64**
|
||||
(Indirection, Shift, Accumulate, Add, and Count) generators.
|
||||
|
||||
- **Additive Lagged Fibonacci Generator**
|
||||
|
||||
These PRNGs can be selected at runtime (see the man page for the exact CLI options) and are used by any wipe method that requires random patterns (for example PRNG Stream, Schneier or BMB21 random passes).
|
||||
|
||||
---
|
||||
|
||||
## I/O subsystem and Direct I/O
|
||||
|
||||
Starting with v0.40 the nwipe I/O layer has been significantly modernised:
|
||||
|
||||
### Large, aligned I/O buffers
|
||||
|
||||
nwipe now uses **large, aligned multi-megabyte buffers** when reading and writing:
|
||||
|
||||
- Reduces the number of `read()` / `write()` calls.
|
||||
- Improves throughput on fast devices (SSD / NVMe / high-speed RAID).
|
||||
- Ensures correct alignment for Direct I/O (O_DIRECT) where supported.
|
||||
|
||||
### I/O mode selection
|
||||
|
||||
You can now explicitly control how nwipe accesses the device:
|
||||
|
||||
```bash
|
||||
--io-mode=auto # default
|
||||
--io-mode=direct # equivalent to --directio
|
||||
--io-mode=cached # equivalent to --cachedio
|
||||
|
||||
--directio # force O_DIRECT (no fallback)
|
||||
--cachedio # force kernel cached I/O only
|
||||
````
|
||||
|
||||
* **auto**
|
||||
Try to open the device with `O_DIRECT`. If the kernel or filesystem does not support it (EINVAL/EOPNOTSUPP), nwipe falls back to cached I/O and logs a warning.
|
||||
|
||||
* **direct** / `--directio`
|
||||
Force Direct I/O. If `O_DIRECT` is not supported for the device, opening the device fails and the wipe will not proceed. This is useful for strict testing and benchmarking.
|
||||
|
||||
* **cached** / `--cachedio`
|
||||
Always use kernel buffered I/O. `O_DIRECT` is never attempted. This is closest to the behaviour of older nwipe releases.
|
||||
|
||||
### Sync behaviour
|
||||
|
||||
The `--sync` option controls how often nwipe performs `fdatasync()` when using **cached I/O**:
|
||||
|
||||
* The value represents the number of **device hardware blocks** (typically 512 or 4096 bytes) between syncs.
|
||||
* Internally this value is scaled to match nwipe’s large I/O buffer size so that the effective **bytes between syncs** remain in a reasonable range (tens to hundreds of MB, depending on the default and device).
|
||||
* This ensures:
|
||||
|
||||
* timely detection of I/O and hardware errors in cached I/O mode, and
|
||||
* good throughput for normal use.
|
||||
|
||||
In **Direct I/O** mode (`--directio` / `--io-mode=direct`), periodic sync is disabled: write errors are reported immediately by `write()`, so `fdatasync()` provides no additional safety.
|
||||
|
||||
See the `nwipe(8)` man page for detailed `--sync` semantics and examples.
|
||||
|
||||
---
|
||||
|
||||
## SSD considerations and limitations
|
||||
|
||||
In its current form, nwipe **cannot fully sanitise** solid state drives (SSDs) of any interface type:
|
||||
|
||||
* SAS / SATA / NVMe
|
||||
* Form factors such as 2.5", 3.5", M.2, PCIe, etc.
|
||||
|
||||
This is due to how SSDs internally manage data:
|
||||
|
||||
* SSDs use wear-levelling and frequently maintain additional, non-host-accessible memory (overprovisioning).
|
||||
* Failed blocks may be remapped to reserved areas that are not directly addressable by the OS.
|
||||
* Many vendors restrict low-level access to these areas to the drive’s own controller and firmware.
|
||||
|
||||
For secure SSD sanitisation, it is strongly recommended to:
|
||||
|
||||
1. Use nwipe / ShredOS **in combination with vendor-specific tools**, for example:
|
||||
|
||||
* manufacturer Secure Erase,
|
||||
* NVMe format / sanitize commands, or
|
||||
* hardware vendor–provided utilities, and
|
||||
2. Independently validate the result, comparing drive contents before and after sanitisation where feasible.
|
||||
|
||||
A list of common SSD vendor tools and guidance can be found in the separate [SSD Guide](ssd-guide.md).
|
||||
|
||||
---
|
||||
|
||||
## Compiling & installing
|
||||
|
||||
For development work, see the [Hacking](#hacking) section below.
|
||||
For a **bootable image** with the latest nwipe master that you can write to a USB stick or CD/DVD, see [Quick & easy, USB bootable version](#quick--easy-usb-bootable-version-of-nwipe-master-for-x86_64-systems).
|
||||
|
||||
### Dependencies
|
||||
|
||||
`nwipe` requires the following libraries:
|
||||
|
||||
* `ncurses`
|
||||
* `pthreads`
|
||||
* `parted`
|
||||
* `libconfig`
|
||||
|
||||
`nwipe` also requires the following program and will abort with a warning if not found:
|
||||
|
||||
* **hdparm** (as of current master and v0.35+)
|
||||
|
||||
The following tools are optional but **strongly recommended**:
|
||||
|
||||
* `dmidecode`
|
||||
* `coreutils` (for `readlink`)
|
||||
* `smartmontools`
|
||||
|
||||
These tools enable features such as:
|
||||
|
||||
* HPA/DCO detection
|
||||
* SMART data for PDF certificates (especially for USB bridge devices)
|
||||
* SMBIOS/DMI host information in the log
|
||||
* Correct bus type detection (ATA/USB/etc.) and proper operation of `--nousb`
|
||||
|
||||
### Debian & Ubuntu prerequisites
|
||||
|
||||
If you are compiling `nwipe` from source, the following libraries will need to be installed first:
|
||||
If you are compiling `nwipe` from source on Debian/Ubuntu:
|
||||
|
||||
```bash
|
||||
sudo apt install \
|
||||
@@ -95,149 +240,251 @@ sudo apt install \
|
||||
dmidecode \
|
||||
coreutils \
|
||||
smartmontools \
|
||||
hdparm
|
||||
hdparm \
|
||||
```
|
||||
|
||||
### Fedora prerequisites
|
||||
### Fedora / RHEL / CentOS Stream prerequisites
|
||||
|
||||
```bash
|
||||
sudo bash
|
||||
dnf update
|
||||
dnf groupinstall "Development Tools"
|
||||
dnf groupinstall "C Development Tools and Libraries"
|
||||
yum install ncurses-devel
|
||||
yum install parted-devel
|
||||
yum install libconfig-devel
|
||||
yum install libconfig++-devel
|
||||
yum install dmidecode
|
||||
yum install coreutils
|
||||
yum install smartmontools
|
||||
yum install hdparm
|
||||
sudo dnf update -y
|
||||
sudo dnf groupinstall -y "Development Tools" "C Development Tools and Libraries"
|
||||
sudo dnf install -y \
|
||||
ncurses-devel \
|
||||
parted-devel \
|
||||
libconfig-devel \
|
||||
libconfig++-devel \
|
||||
dmidecode \
|
||||
coreutils \
|
||||
smartmontools \
|
||||
hdparm
|
||||
```
|
||||
Note. The following programs are optionally installed although recommended. 1. dmidecode 2. readlink 3. smartmontools.
|
||||
|
||||
#### hdparm [REQUIRED]
|
||||
hdparm provides some of the information regarding disk size in sectors as related to the host protected area (HPA) and device configuration overlay (DCO). We do however have our own function that directly access the DCO to obtain the 'real max sectors' so reliance on hdparm may be removed at a future date.
|
||||
### Arch Linux / Manjaro prerequisites
|
||||
|
||||
#### dmidecode [RECOMMENDED]
|
||||
dmidecode provides SMBIOS/DMI host data to stdout or the log file. If you don't install it you won't see the SMBIOS/DMI host data at the beginning of nwipes log.
|
||||
```bash
|
||||
sudo pacman -Syu --needed \
|
||||
base-devel \
|
||||
ncurses \
|
||||
parted \
|
||||
libconfig \
|
||||
dmidecode \
|
||||
coreutils \
|
||||
smartmontools \
|
||||
hdparm
|
||||
```
|
||||
### openSUSE (Leap / Tumbleweed) prerequisites
|
||||
|
||||
#### coreutils (provides readlink) [RECOMMENDED]
|
||||
readlink determines the bus type, i.e. ATA, USB etc. Without it the --nousb option won't work and bus type information will be missing from nwipes selection and wipe windows. The coreutils package is often automatically installed as default in most if not all distros.
|
||||
```bash
|
||||
sudo zypper refresh
|
||||
sudo zypper install -y \
|
||||
gcc \
|
||||
make \
|
||||
automake \
|
||||
autoconf \
|
||||
libtool \
|
||||
ncurses-devel \
|
||||
libparted-devel \
|
||||
libconfig-devel \
|
||||
libconfig++-devel \
|
||||
dmidecode \
|
||||
coreutils \
|
||||
smartmontools \
|
||||
hdparm
|
||||
```
|
||||
|
||||
#### smartmontools [REQUIRED]
|
||||
smartmontools obtains serial number information for supported USB to IDE/SATA adapters. Without it, drives plugged into USB ports will not show serial number information.
|
||||
|
||||
If you want a quick and easy way to keep your copy of nwipe running the latest master release of nwipe see the [automating the download and compilation](#automating-the-download-and-compilation-process-for-debian-based-distros) section.
|
||||
Note: `dmidecode`, `readlink` (from `coreutils`) and `smartmontools` are technically optional, but recommended for full feature support.
|
||||
|
||||
### Compilation
|
||||
|
||||
First create all the autoconf files:
|
||||
```
|
||||
Generate the autoconf files:
|
||||
|
||||
```bash
|
||||
./autogen.sh
|
||||
```
|
||||
|
||||
Then compile & install using the following standard commands:
|
||||
```
|
||||
Then configure, build and install:
|
||||
|
||||
```bash
|
||||
./configure
|
||||
make format (only required if submitting pull requests)
|
||||
make format # only required if you intend to submit pull requests
|
||||
make
|
||||
make install
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Then run nwipe !
|
||||
```
|
||||
Run nwipe:
|
||||
|
||||
```bash
|
||||
cd src
|
||||
sudo ./nwipe
|
||||
```
|
||||
or
|
||||
```
|
||||
|
||||
or simply:
|
||||
|
||||
```bash
|
||||
sudo nwipe
|
||||
```
|
||||
|
||||
### Hacking
|
||||
---
|
||||
|
||||
If you wish to submit pull requests to this code we would prefer you enable all warnings when compiling.
|
||||
This can be done using the following compile commands:
|
||||
## Hacking
|
||||
|
||||
```
|
||||
If you intend to submit patches or pull requests, we recommend enabling full warnings in your development build.
|
||||
|
||||
For a debug–friendly build:
|
||||
|
||||
```bash
|
||||
./configure --prefix=/usr CFLAGS='-O0 -g -Wall -Wextra'
|
||||
make format (necessary if submitting pull requests)
|
||||
make format # necessary if submitting pull requests
|
||||
make
|
||||
make install
|
||||
sudo make install
|
||||
```
|
||||
|
||||
The `-O0 -g` flags disable optimisations. This is required if you're debugging with `gdb` in an IDE such as Kdevelop. With these optimisations enabled you won't be able to see the values of many variables in nwipe, not to mention the IDE won't step through the code properly.
|
||||
* `-O0 -g`
|
||||
Disables optimisation and includes debug symbols. This is important if you are debugging with `gdb` or an IDE (e.g. KDevelop), and want accurate stepping and variable inspection.
|
||||
|
||||
The `-Wall` and `-Wextra` flags enable all compiler warnings. Please submit code with zero warnings.
|
||||
* `-Wall -Wextra`
|
||||
Enables most useful compiler warnings. Please submit code with **zero warnings**.
|
||||
|
||||
Also make sure that your changes are consistent with the coding style defined in the `.clang-format` file, using:
|
||||
```
|
||||
The code style is defined via `.clang-format`. Before submitting:
|
||||
|
||||
```bash
|
||||
make format
|
||||
```
|
||||
You will need `clang-format` installed to use the `format` command.
|
||||
|
||||
Once done with your coding then the released/patch/fixed code can be compiled, with all the normal optimisations, using:
|
||||
```
|
||||
./configure --prefix=/usr && make && make install
|
||||
You will need `clang-format` installed for this step.
|
||||
|
||||
To rebuild a "release-like" binary with normal optimisations after development:
|
||||
|
||||
```bash
|
||||
./configure --prefix=/usr
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
## Automating the download and compilation process for Debian based distros.
|
||||
---
|
||||
|
||||
Here's a script that will do just that! It will create a directory in your home folder called 'nwipe_master'. It installs all the libraries required to compile the software (build-essential) and all the libraries that nwipe requires (libparted etc). It downloads the latest master copy of nwipe from github. It then compiles the software and then runs the latest nwipe. It doesn't write over the version of nwipe that's installed in the repository (If you had nwipe already installed). To run the latest master version of nwipe manually you would run it like this `sudo ~/nwipe_master/nwipe/src/nwipe`
|
||||
## Automating download and compilation (Debian-based distros)
|
||||
|
||||
You can run the script multiple times; the first time it's run it will install all the libraries; subsequent times it will just say the libraries are up to date. As it always downloads a fresh copy of the nwipe master from Github, you can always stay up to date. Just run it to get the latest version of nwipe. It only takes 11 seconds to compile on my i7.
|
||||
Below is a convenience script that:
|
||||
|
||||
If you already have nwipe installed from the repository, you need to take care which version you are running. If you typed `nwipe` from any directory it will always run the original repository copy of nwipe. To run the latest nwipe you have to explicitly tell it where the new copy is, e.g in the directory `~/nwipe_master/nwipe/src`. That's why you would run it by typing `sudo ~/nwipe_master/nwipe/src/nwipe` alternatively you could cd to the directory and run it like this:
|
||||
1. Creates `~/nwipe_master`
|
||||
2. Installs all required build dependencies
|
||||
3. Downloads the latest master branch from GitHub
|
||||
4. Builds nwipe
|
||||
5. Runs the freshly built nwipe
|
||||
|
||||
```
|
||||
cd ~/nwipe_master/nwipe/src
|
||||
./nwipe
|
||||
```
|
||||
It does **not** overwrite any nwipe installed from your distribution’s repository.
|
||||
|
||||
Note the ./, that means only look in the current directory for nwipe. if you forgot to type ./ the computer would run the older repository version of nwipe.
|
||||
Save the following as `buildnwipe`, then make it executable with `chmod +x buildnwipe`:
|
||||
|
||||
Once you have copied the script below into a file called buildnwipe, you need to give the file execute permissions `chmod +x buildnwipe` before you can run it.
|
||||
```
|
||||
```bash
|
||||
#!/bin/bash
|
||||
cd "$HOME"
|
||||
nwipe_directory="nwipe_master"
|
||||
mkdir $nwipe_directory
|
||||
cd $nwipe_directory
|
||||
sudo apt install build-essential pkg-config automake libncurses5-dev autotools-dev libparted-dev libconfig-dev libconfig++-dev dmidecode readlink smartmontools git
|
||||
mkdir -p "$nwipe_directory"
|
||||
cd "$nwipe_directory"
|
||||
|
||||
sudo apt install -y \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
automake \
|
||||
libncurses5-dev \
|
||||
autotools-dev \
|
||||
libparted-dev \
|
||||
libconfig-dev \
|
||||
libconfig++-dev \
|
||||
dmidecode \
|
||||
readlink \
|
||||
smartmontools \
|
||||
hdparm \
|
||||
git
|
||||
|
||||
rm -rf nwipe
|
||||
git clone https://github.com/martijnvanbrummelen/nwipe.git
|
||||
cd "nwipe"
|
||||
cd nwipe
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
cd "src"
|
||||
|
||||
cd src
|
||||
sudo ./nwipe
|
||||
```
|
||||
|
||||
## Quick & Easy, USB bootable version of nwipe master for x86_64 systems.
|
||||
If you want to just try out a bootable version of nwipe you can download [ShredOS](https://github.com/PartialVolume/shredos.x86_64). The ShredOS image is around 60MB and can be written to an USB flash drive in seconds, allowing you to boot straight into the latest version of nwipe. ShredOS is available for x86_64 (64-bit) and i686 (32-bit) CPU architectures and will boot both legacy BIOS and UEFI devices. It comes as .IMG (bootable USB flash drive image) or .ISO (for CD-R/DVD-R). Instructions and download can be found [here](https://github.com/PartialVolume/shredos.x86_64#obtaining-and-writing-shredos-to-a-usb-flash-drive-the-easy-way-).
|
||||
To run the latest master later on:
|
||||
|
||||
```bash
|
||||
sudo ~/nwipe_master/nwipe/src/nwipe
|
||||
```
|
||||
|
||||
If you already have nwipe from your distro’s repo installed, remember:
|
||||
|
||||
* `nwipe` → runs the packaged version in your `$PATH`
|
||||
* `./nwipe` in `~/nwipe_master/nwipe/src` → runs the freshly built master
|
||||
|
||||
---
|
||||
|
||||
## Quick & easy, USB bootable version of nwipe master for x86_64 systems
|
||||
|
||||
If you prefer a bootable image containing the latest nwipe master, use **ShredOS**:
|
||||
|
||||
* [ShredOS](https://github.com/PartialVolume/shredos.x86_64)
|
||||
|
||||
ShredOS:
|
||||
|
||||
* is ~60 MB in size,
|
||||
* can be written to a USB flash drive in seconds,
|
||||
* boots directly into a minimal environment running the latest nwipe,
|
||||
* is available for x86_64 (64-bit) and i686 (32-bit),
|
||||
* supports both legacy BIOS and UEFI.
|
||||
|
||||
It is provided as:
|
||||
|
||||
* `.img` (USB bootable image), and
|
||||
* `.iso` (for CD/DVD).
|
||||
|
||||
See the ShredOS README for detailed instructions on downloading and writing the image.
|
||||
|
||||
---
|
||||
|
||||
## Which Linux distro uses the latest nwipe?
|
||||
See [Repology](https://repology.org/project/nwipe/versions)
|
||||
|
||||
And in addition checkout the following distros that all include nwipe:
|
||||
You can see an overview at:
|
||||
|
||||
- [ShredOS](https://github.com/PartialVolume/shredos.x86_64) Always has the latest nwipe release.
|
||||
- [netboot.xyz](https://github.com/netbootxyz/netboot.xyz) Can network-boot ShredOS.
|
||||
- [DiskDump](https://github.com/Awire9966/DiskDump) nwipe on Debian livecd, can wipe eMMC chips.
|
||||
- [partedmagic](https://partedmagic.com)
|
||||
- [SystemRescueCD](https://www.system-rescue.org)
|
||||
- [gparted live](https://sourceforge.net/projects/gparted/files/gparted-live-testing/1.2.0-2/)
|
||||
- [grml](https://grml.org/)
|
||||
* [Repology](https://repology.org/project/nwipe/versions)
|
||||
|
||||
Know of other distros that include nwipe? Then please let us know or issue a PR on this README.md. Thanks.
|
||||
Distributions known to include nwipe:
|
||||
|
||||
* [ShredOS](https://github.com/PartialVolume/shredos.x86_64) – always ships the latest nwipe release.
|
||||
* [netboot.xyz](https://github.com/netbootxyz/netboot.xyz) – can network-boot ShredOS.
|
||||
* [DiskDump](https://github.com/Awire9966/DiskDump) – nwipe on a Debian live CD, can wipe eMMC.
|
||||
* [Parted Magic](https://partedmagic.com)
|
||||
* [SystemRescue](https://www.system-rescue.org)
|
||||
* [GParted Live](https://sourceforge.net/projects/gparted/files/gparted-live-testing/1.2.0-2/)
|
||||
* [Grml](https://grml.org/)
|
||||
|
||||
If you know of other distributions that ship nwipe, please let us know or send a PR updating this README.
|
||||
|
||||
---
|
||||
|
||||
## Bugs
|
||||
|
||||
Bugs can be reported on GitHub:
|
||||
https://github.com/martijnvanbrummelen/nwipe
|
||||
Bugs, feature requests, and pull requests are welcome on GitHub:
|
||||
|
||||
* [https://github.com/martijnvanbrummelen/nwipe](https://github.com/martijnvanbrummelen/nwipe)
|
||||
|
||||
Please include:
|
||||
|
||||
* your distribution and version,
|
||||
* the nwipe version (or git commit hash),
|
||||
* hardware details (especially for I/O-related issues),
|
||||
* log output and command line options used.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v2.0
|
||||
nwipe is licensed under the **GNU General Public License v2.0**.
|
||||
See the `LICENSE` file for details.
|
||||
|
||||
|
||||
|
||||
12
configure.ac
12
configure.ac
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.63])
|
||||
AC_INIT([nwipe],[0.38],[git@brumit.nl])
|
||||
AC_INIT([nwipe],[0.40],[git@brumit.nl])
|
||||
AM_INIT_AUTOMAKE(foreign subdir-objects)
|
||||
AC_CONFIG_FILES([Makefile src/Makefile man/Makefile])
|
||||
AC_OUTPUT
|
||||
@@ -11,6 +11,7 @@ AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
# Checks for libraries.
|
||||
@@ -67,8 +68,13 @@ PKG_CHECK_MODULES(
|
||||
], [AC_MSG_ERROR([libconfig library not found])])]
|
||||
)
|
||||
|
||||
AC_CHECK_LIB([intl], [libintl_dgettext]) # needed to statically link libparted, but not given in its pkgconfig file
|
||||
AC_CHECK_LIB([uuid], [uuid_generate]) # needed to statically link libparted, but not given in its pkgconfig file
|
||||
# Libraries needed for static linking with parted
|
||||
if echo "$LDFLAGS" | grep -q "static"; then
|
||||
AC_CHECK_LIB([uuid], [uuid_generate], [LIBS="-luuid $LIBS"], [AC_MSG_ERROR([libuuid is required for static linking but not found])])
|
||||
AC_CHECK_LIB([devmapper], [dm_task_create], [LIBS="-ldevmapper $LIBS"], [AC_MSG_ERROR([libdevmapper is required for static linking but not found])])
|
||||
AC_CHECK_LIB([blkid], [blkid_new_probe], [LIBS="-lblkid $LIBS"], [AC_MSG_ERROR([libblkid is required for static linking but not found])])
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES([PARTED], [libparted])
|
||||
AC_CHECK_LIB([pthread], [main], ,[AC_MSG_ERROR([pthread development library not found])])
|
||||
|
||||
|
||||
146
man/nwipe.8
146
man/nwipe.8
@@ -1,4 +1,4 @@
|
||||
.TH NWIPE "8" "Jan 2025" "nwipe version 0.38" "User Commands"
|
||||
.TH NWIPE "5" "Feb 2026" "nwipe version 0.40" "User Commands"
|
||||
.SH NAME
|
||||
nwipe \- securely erase disks
|
||||
.SH SYNOPSIS
|
||||
@@ -6,28 +6,50 @@ nwipe \- securely erase disks
|
||||
[\fIoptions\fR] [\fIdevice1\fR] [\fIdevice2\fR] ...
|
||||
.SH DESCRIPTION
|
||||
nwipe is a command that will securely erase disks using a variety of
|
||||
recognised methods. It is a fork of the dwipe command used by Darik's Boot
|
||||
and Nuke (dban). nwipe is included with partedmagic if you want a quick and
|
||||
recognized methods. It is a fork of the dwipe command used by Darik's Boot
|
||||
and Nuke (DBAN). nwipe is included with partedmagic if you want a quick and
|
||||
easy bootable CD version. nwipe was created out of a need to run the DBAN
|
||||
dwipe command outside of DBAN, in order to allow its use with any host
|
||||
distribution, thus giving better hardware support. It is essentially the
|
||||
same as dwipe, with a few changes:
|
||||
.TP
|
||||
- pthreads is used instead of fork
|
||||
- pthreads is used instead of fork.
|
||||
.TP
|
||||
- The parted library is used to detect drives
|
||||
- The parted library is used to detect drives.
|
||||
.TP
|
||||
- The code is designed to be compiled with gcc
|
||||
- The code is designed to be compiled with gcc.
|
||||
.TP
|
||||
- SIGUSR1 can be used to log the stats of the current wipe
|
||||
- SIGUSR1 can be used to log the stats of the current wipe.
|
||||
.TP
|
||||
- Additional wiping methods and PRNGs.
|
||||
.TP
|
||||
- Configurable I/O modes (cached, direct, auto) using large I/O buffers for higher throughput.
|
||||
.TP
|
||||
- Support for stable device paths such as \fI/dev/disk/by-id/\fR.
|
||||
.PP
|
||||
All PRNG implementations are seeded using the Linux
|
||||
.BR getrandom (2)
|
||||
system call instead of reading from
|
||||
.IR /dev/urandom
|
||||
via a file descriptor.
|
||||
|
||||
.SH DEVICES
|
||||
.PP
|
||||
Devices can be specified either as block device nodes (for example
|
||||
.IR /dev/sda ,
|
||||
.IR /dev/nvme0n1 ,
|
||||
.IR /dev/mapper/cryptroot )
|
||||
or via stable symlinks under
|
||||
.IR /dev/disk/by-id/ .
|
||||
nwipe will resolve these paths and operate on the underlying block device.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-version\fR
|
||||
Prints the version number
|
||||
Prints the version number.
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Prints a help summary
|
||||
Prints a help summary.
|
||||
.TP
|
||||
\fB\-\-autonuke\fR
|
||||
If no devices have been specified on the command line, starts wiping all
|
||||
@@ -39,17 +61,54 @@ Power off system on completion of wipe delayed for one minute. During
|
||||
this one minute delay you can abort the shutdown by typing sudo shutdown -c
|
||||
.TP
|
||||
\fB\-\-sync\fR=\fINUM\fR
|
||||
Will perform a syn after NUM writes (default: 100000)
|
||||
Specify how often nwipe performs an fdatasync() during cached I/O mode.
|
||||
The value refers to the number of *device hardware blocks* (commonly 512 or
|
||||
4096 bytes) written before triggering a sync. Since nwipe now writes using
|
||||
large multi-megabyte buffers, this value is automatically scaled so the sync
|
||||
interval in bytes is consistent with historic behaviour.
|
||||
|
||||
The default value (100000) results in a sync approximately every 50–400 MB,
|
||||
similar to earlier nwipe releases. This ensures timely detection of I/O errors
|
||||
while maintaining good throughput.
|
||||
|
||||
This setting has no effect when using --directio, as write() returns errors
|
||||
immediately under direct I/O.
|
||||
|
||||
.IP
|
||||
0 \- fdatasync after the disk is completely written
|
||||
fdatasync errors not detected until completion.
|
||||
0 is not recommended as disk errors may cause nwipe
|
||||
to appear to hang
|
||||
0 \- Perform one sync only at the end of the pass.
|
||||
Not advised; errors may only be detected after the entire wipe.
|
||||
|
||||
.IP
|
||||
1 \- fdatasync after every write
|
||||
Warning: Lower values will reduce wipe speeds.
|
||||
1 \- Sync immediately after each write.
|
||||
Extremely safe but extremely slow.
|
||||
|
||||
.IP
|
||||
1000 \- fdatasync after 1000 writes
|
||||
1000 \- Sync after the equivalent of 1000 hardware blocks.
|
||||
Useful for testing or more aggressive error detection.
|
||||
.TP
|
||||
\fB\-\-cachedio\fR
|
||||
Use buffered I/O with large write buffers (page cache enabled). This is the
|
||||
default on most systems and generally gives the best performance for
|
||||
rotational disks.
|
||||
.TP
|
||||
\fB\-\-directio\fR
|
||||
Use direct I/O with large write buffers. This opens devices with
|
||||
.BR O_DIRECT
|
||||
to bypass the page cache. It can be useful when running multiple wipes in
|
||||
parallel or when you do not want to pollute the system page cache. On some
|
||||
devices this may be slower than cached I/O.
|
||||
.TP
|
||||
\fB\-\-io\-mode\fR=\fIMODE\fR
|
||||
Select the I/O mode explicitly. \fIMODE\fR can be:
|
||||
.IP
|
||||
\fBauto\fR \- (default) automatically choose the best supported mode for
|
||||
the device and kernel.
|
||||
.IP
|
||||
\fBcached\fR \- force buffered I/O.
|
||||
.IP
|
||||
\fBdirect\fR \- force direct I/O (\fBO_DIRECT\fR).
|
||||
.IP
|
||||
Large I/O buffers are used in all modes to maximise throughput.
|
||||
.TP
|
||||
\fB\-\-noblank\fR
|
||||
Do not perform the final blanking pass after the wipe (default is to blank,
|
||||
@@ -70,46 +129,58 @@ Do not show the GUI interface. Can only be used with the autonuke option.
|
||||
Nowait option is automatically invoked with the nogui option.
|
||||
SIGUSR1 can be used to retrieve the current wiping statistics.
|
||||
.TP
|
||||
\fB\-\-pdftag\fR
|
||||
Enables a field on the PDF that holds a tag that identifies the host computer
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-verbose\fR
|
||||
Log more messages, useful for debugging.
|
||||
.TP
|
||||
\fB\-\-verify\fR=\fITYPE\fR
|
||||
Whether to perform verification of erasure (default: last)
|
||||
Whether to perform verification of erasure (default: last).
|
||||
.IP
|
||||
off \- Do not verify
|
||||
off \- Do not verify.
|
||||
.IP
|
||||
last \- Verify after the last pass
|
||||
last \- Verify after the last pass.
|
||||
.IP
|
||||
all \- Verify every pass
|
||||
all \- Verify every pass.
|
||||
.IP
|
||||
Please mind that HMG IS5 enhanced always verifies the last (PRNG) pass
|
||||
regardless of this option.
|
||||
.TP
|
||||
\fB\-m\fR, \fB\-\-method\fR=\fIMETHOD\fR
|
||||
The wiping method (default: dodshort).
|
||||
The wiping method (default: prng).
|
||||
.IP
|
||||
dod522022m / dod \- 7 pass DOD 5220.22\-M method
|
||||
.IP
|
||||
dodshort / dod3pass \- 3 pass DOD method
|
||||
.IP
|
||||
gutmann \- Peter Gutmann's Algorithm
|
||||
gutmann \- Peter Gutmann's algorithm
|
||||
.IP
|
||||
ops2 \- RCMP TSSIT OPS\-II
|
||||
.IP
|
||||
random / prng / stream \- PRNG Stream
|
||||
.IP
|
||||
zero / quick \- Overwrite with zeros 0x00
|
||||
zero / quick \- Overwrite with zeros (0x00)
|
||||
.IP
|
||||
one \- Overwrite with ones 0xFF
|
||||
one \- Overwrite with ones (0xFF)
|
||||
.IP
|
||||
verify_zero \- Verifies disk is zero filled
|
||||
verify_zero \- Verifies disk is zero (0x00) filled
|
||||
.IP
|
||||
verify_one \- Verifies disk is 0xFF filled
|
||||
verify_one \- Verifies disk is one (0xFF) filled
|
||||
.IP
|
||||
is5enh \- HMG IS5 enhanced
|
||||
.IP
|
||||
bruce7 \- Schneier Bruce 7-pass mixed pattern
|
||||
.IP
|
||||
bmb \- Chinese BMB21-2019 State Secrets Bureau standard.
|
||||
This method overwrites the device with ones (0xFF),
|
||||
then zeros (0x00), followed by three passes of PRNG-
|
||||
generated random data, and finishes with a final pass
|
||||
of ones (0xFF). Designed to meet the BMB21-2019
|
||||
technical sanitization requirements.
|
||||
.TP
|
||||
\fB\-l\fR, \fB\-\-logfile\fR=\fIFILE\fR
|
||||
Filename to log to. Default is STDOUT
|
||||
Filename to log to. Default is STDOUT.
|
||||
.TP
|
||||
\fB\-P\fR, \fB\-\-PDFreportpath\fR=\fIDIR\fR
|
||||
Directory to write the PDF nwipe reports/certificates to.
|
||||
@@ -117,26 +188,35 @@ Defaults to ".".
|
||||
If \fIDIR\fR is set to \fInoPDF\fR no report PDF files are written.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-prng\fR=\fIMETHOD\fR
|
||||
PRNG option (mersenne|twister|isaac|isaac64)
|
||||
The PRNG option (default: aes_ctr_prng).
|
||||
(mersenne|twister|isaac|isaac64|add_lagg_fibonacci_prng|xoroshiro256_prng|aes_ctr_prng)
|
||||
.IP
|
||||
\fBaes_ctr_prng\fR uses the Linux kernel AF_ALG interface to AES\-CTR as a
|
||||
cryptographically strong stream generator. It is seeded via
|
||||
.BR getrandom (2)
|
||||
and requires kernel crypto support for AES\-CTR.
|
||||
.TP
|
||||
\fB\-q\fR, \fB\-\-quiet\fR
|
||||
Anonymize serial numbers, Gui & logs display:
|
||||
Anonymize serial numbers, GUI & logs display:
|
||||
XXXXXXXX = S/N obtained & anonymized.
|
||||
???????? = S/N not available.
|
||||
.TP
|
||||
\fB\-r\fR, \fB\-\-rounds\fR=\fINUM\fR
|
||||
Number of times to wipe the device using the selected method (default: 1)
|
||||
Number of times to wipe the device using the selected method (default: 1).
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-exclude\fR=\fIDEVICES\fR
|
||||
Up to ten comma separated devices to be excluded, examples:
|
||||
--exclude=/dev/sdc
|
||||
--exclude=/dev/sdc,/dev/sdd
|
||||
--exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1
|
||||
--dev/disk/by-path/pci-0000:00:17.0-ata-1
|
||||
.SH BUGS
|
||||
Please see the GitHub site for the latest list
|
||||
Please see the GitHub site for the latest list:
|
||||
(https://github.com/martijnvanbrummelen/nwipe/issues)
|
||||
|
||||
.SH AUTHOR
|
||||
Nwipe is developed by Martijn van Brummelen <github@brumit.nl>
|
||||
nwipe is developed by Martijn van Brummelen <github@brumit.nl>.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.BR shred (1),
|
||||
.BR dwipe (1),
|
||||
|
||||
@@ -6,5 +6,5 @@ AM_LDFLAGS =
|
||||
# this lists the binaries to produce, the (non-PHONY, binary) targets in
|
||||
# the previous manual Makefile
|
||||
bin_PROGRAMS = nwipe
|
||||
nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h alfg/add_lagg_fibonacci_prng.h alfg/add_lagg_fibonacci_prng.c xor/xoroshiro256_prng.h xor/xoroshiro256_prng.c pass.h device.h logging.c method.c options.c prng.c version.c temperature.c PDFGen/pdfgen.h PDFGen/pdfgen.c create_pdf.c create_pdf.h embedded_images/shred_db.jpg.c embedded_images/shred_db.jpg.h embedded_images/tick_erased.jpg.c embedded_images/tick_erased.jpg.h embedded_images/redcross.c embedded_images/redcross.h hpa_dco.h hpa_dco.c miscellaneous.h miscellaneous.c embedded_images/nwipe_exclamation.jpg.h embedded_images/nwipe_exclamation.jpg.c conf.h conf.c customers.h customers.c hddtemp_scsi/hddtemp.h hddtemp_scsi/scsi.h hddtemp_scsi/scsicmds.h hddtemp_scsi/get_scsi_temp.c hddtemp_scsi/scsi.c hddtemp_scsi/scsicmds.c
|
||||
nwipe_SOURCES = context.h logging.h options.h prng.h version.h temperature.h nwipe.c gui.c method.h pass.c device.c gui.h isaac_rand/isaac_standard.h isaac_rand/isaac_rand.h isaac_rand/isaac_rand.c isaac_rand/isaac64.h isaac_rand/isaac64.c mt19937ar-cok/mt19937ar-cok.c nwipe.h mt19937ar-cok/mt19937ar-cok.h alfg/add_lagg_fibonacci_prng.h alfg/add_lagg_fibonacci_prng.c xor/xoroshiro256_prng.h xor/xoroshiro256_prng.c aes/aes_ctr_prng.h aes/aes_ctr_prng.cpp pass.h device.h logging.c method.c options.c prng.c version.c temperature.c PDFGen/pdfgen.h PDFGen/pdfgen.c create_pdf.c create_pdf.h embedded_images/shred_db.jpg.c embedded_images/shred_db.jpg.h embedded_images/tick_erased.jpg.c embedded_images/tick_erased.jpg.h embedded_images/redcross.c embedded_images/redcross.h hpa_dco.h hpa_dco.c miscellaneous.h miscellaneous.c embedded_images/nwipe_exclamation.jpg.h embedded_images/nwipe_exclamation.jpg.c conf.h conf.c customers.h customers.c hddtemp_scsi/hddtemp.h hddtemp_scsi/scsi.h hddtemp_scsi/scsicmds.h hddtemp_scsi/get_scsi_temp.c hddtemp_scsi/scsi.c hddtemp_scsi/scsicmds.c cpu_features.h cpu_features.c
|
||||
nwipe_LDADD = $(PARTED_LIBS) $(LIBCONFIG)
|
||||
|
||||
425
src/aes/aes_ctr_prng.cpp
Normal file
425
src/aes/aes_ctr_prng.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief High-throughput AES-CTR PRNG for nwipe using Linux AF_ALG.
|
||||
*
|
||||
* @details
|
||||
* This translation unit implements a cryptographically strong pseudorandom
|
||||
* byte stream based on AES-CTR, leveraging the Linux kernel's crypto API
|
||||
* (AF_ALG) for hardware-accelerated AES (AES-NI/VAES/NEON/SVE where available).
|
||||
*
|
||||
* Motivation:
|
||||
* - nwipe must supply multi-GB/s of random data to saturate modern NVMe/RAID.
|
||||
* - User-space OpenSSL-based paths in older builds plateaued around ~250 MB/s
|
||||
* on some systems due to syscall/memory-copy patterns not tuned for the
|
||||
* workload.
|
||||
* - The kernel provides highly optimized AES implementations and scheduling.
|
||||
*
|
||||
* Key properties:
|
||||
* - A single AF_ALG operation socket is opened *once per thread* and reused
|
||||
* for all generation calls (low syscall overhead).
|
||||
* - Each generation produces a fixed-size chunk (see CHUNK) by issuing exactly
|
||||
* two syscalls: `sendmsg()` (to supply IV and length) and `read()` (to fetch
|
||||
* the keystream).
|
||||
* - Counter management (increment) is done in user space for determinism.
|
||||
*
|
||||
* @warning
|
||||
* IV (Counter) Encoding:
|
||||
* This implementation builds the 128-bit AES-CTR IV by storing two 64-bit
|
||||
* limbs in **little-endian** order (low limb at IV[0..7], high limb at
|
||||
* IV[8..15]) and then incrementing the 128-bit value in little-endian form.
|
||||
* This deviates from the big-endian counter semantics commonly assumed by
|
||||
* RFC-style AES-CTR specifications. The stream remains secure (uniqueness
|
||||
* of IVs is preserved) but is **not interoperable** with generic RFC-CTR
|
||||
* streams. See `aes_ctr_prng.h` for a prominent header-level note.
|
||||
*
|
||||
* Threading:
|
||||
* - `tls_op_fd` is thread-local; each thread owns its kernel op-socket.
|
||||
* - The kernel cipher is re-entrant. No shared state in this TU is writable
|
||||
* across threads.
|
||||
*
|
||||
* Error handling:
|
||||
* - Functions return `0` on success and `-1` on failure. When underlying
|
||||
* syscalls fail, `-1` is returned; callers may consult `errno` as usual.
|
||||
*/
|
||||
|
||||
// ============================================================================================
|
||||
// WHY THIS FILE EXISTS
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// nwipe, a secure disk-wiping tool, needs cryptographically strong random data at multi-GB/s
|
||||
// in order to keep up with today’s NVMe and RAID arrays. Users complained when the classic
|
||||
// user-space OpenSSL path plateaued around ~250 MB/s on modern CPUs. The Linux kernel
|
||||
// already ships an extremely fast AES implementation (with transparent AES-NI / VAES / NEON
|
||||
// acceleration) that can be accessed from user space via the AF_ALG socket family. By
|
||||
// delegating the heavy crypto to the kernel we gain all of the following *for free*:
|
||||
// • Perfectly tuned instruction selection per CPU (AES-NI, VAES, SVE, etc.)
|
||||
// • Full cache-line prefetch scheduling written by kernel crypto maintainers
|
||||
// • Zero-copy when the cipher runs in the same core
|
||||
// • Automatic fall-back to software if the CPU lacks AES-NI
|
||||
//
|
||||
// DESIGN OVERVIEW (TL;DR)
|
||||
// ----------------------
|
||||
// ┌─ userspace ───────────────────────────────────────────────────────────────────────────────┐
|
||||
// │ +-------------------------------+ │
|
||||
// │ nwipe | aes_ctr_state_t (256 bit) | (1) initialise, store key+counter │
|
||||
// │ +-------------------------------+ │
|
||||
// │ │ ▲ │
|
||||
// │ │ (2) sendmsg() + read() per fixed-size chunk │ │
|
||||
// └─────────────────────┼───────────────────────────────────────────────────────────┤ kernel │
|
||||
// │ │ space │
|
||||
// persistent FD ▼ │ │
|
||||
// ┌──────────────────────┐ │ │
|
||||
// │ AF_ALG op socket │ (ctr(aes)) │ │
|
||||
// └──────────────────────┘ └─────────┘
|
||||
//
|
||||
// Public ABI stability:
|
||||
// ---------------------
|
||||
// The fd is *not* part of the public state (preserves libnwipe ABI). A TU-local,
|
||||
// thread-local descriptor is used internally; multiple PRNG instances per thread
|
||||
// share the same op-socket as needed.
|
||||
//
|
||||
// Safety / threading:
|
||||
// -------------------
|
||||
// • The kernel cipher is re-entrant. Thread-local fd avoids cross-thread hazards.
|
||||
// • Counter increments occur in user space; one aes_ctr_state_t per thread.
|
||||
// ==============================================================================================
|
||||
|
||||
#include "aes_ctr_prng.h" // public header (256-bit state, extern "C" API)
|
||||
#include <sys/socket.h> // socket(), bind(), accept(), sendmsg()
|
||||
#include <linux/if_alg.h> // AF_ALG constants and skcipher API
|
||||
#include <unistd.h> // read(), close()
|
||||
#include <cstring> // memcpy(), memset(), strcpy()
|
||||
#include <array> // std::array for control buffer
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// CONFIGURABLE CHUNK SIZE
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// The per-call output size (CHUNK) can be configured at compile time via
|
||||
// AES_CTR_PRNG_CHUNK_BYTES. Default is 128 KiB.
|
||||
// Example:
|
||||
// gcc -DAES_CTR_PRNG_CHUNK_BYTES="(64u*1024u)" ...
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
#ifndef AES_CTR_PRNG_CHUNK_BYTES
|
||||
#define AES_CTR_PRNG_CHUNK_BYTES (128u * 1024u) // 128 KiB default
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// GLOBAL 256-BIT KEY
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// • Loaded from user-supplied seed in aes_ctr_prng_init().
|
||||
// • Intended to remain constant for the process lifetime (or until re-init).
|
||||
// • Exposed (non-static) so out-of-TU tests can assert correct key handling.
|
||||
//
|
||||
// @note Consider zeroizing on shutdown to avoid key retention in core dumps.
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
unsigned char global_key[32];
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// THREAD-LOCAL OPERATION SOCKET (one per nwipe thread)
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// Portable TLS qualifier: C++11 `thread_local` or GCC/Clang `__thread` for C builds.
|
||||
//
|
||||
// @invariant tls_op_fd == -1 ⇒ this thread has not opened the op-socket yet.
|
||||
// tls_op_fd >= 0 ⇒ valid AF_ALG operation socket for "ctr(aes)".
|
||||
//
|
||||
// @thread_safety Thread-local; no synchronization required.
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
#if defined(__cplusplus) && __cplusplus >= 201103L
|
||||
#define PRNG_THREAD_LOCAL thread_local
|
||||
#else
|
||||
#define PRNG_THREAD_LOCAL __thread
|
||||
#endif
|
||||
|
||||
PRNG_THREAD_LOCAL static int tls_op_fd = -1; // -1 ⇒ not yet opened in this thread
|
||||
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
// CONSTANTS / INTERNAL HELPERS
|
||||
// ----------------------------------------------------------------------------------------------
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief AES block size in bytes (by specification).
|
||||
*/
|
||||
constexpr std::size_t AES_BLOCK = 16u;
|
||||
|
||||
/**
|
||||
* @brief Fixed-size generation granularity per kernel call.
|
||||
* @details
|
||||
* Adjust at build time via AES_CTR_PRNG_CHUNK_BYTES to balance syscall
|
||||
* overhead vs. latency and memory traffic.
|
||||
* Typical values: 16 KiB (legacy default), 64 KiB, 128 KiB.
|
||||
*/
|
||||
constexpr std::size_t CHUNK = AES_CTR_PRNG_CHUNK_BYTES;
|
||||
|
||||
static_assert(CHUNK % AES_BLOCK == 0,
|
||||
"AES_CTR_PRNG_CHUNK_BYTES must be a multiple of AES_BLOCK (16 bytes)");
|
||||
|
||||
/// Number of AES-CTR blocks produced per CHUNK.
|
||||
constexpr std::size_t BLOCKS_PER_CHUNK = CHUNK / AES_BLOCK;
|
||||
|
||||
/**
|
||||
* @brief Store a 64-bit integer in little-endian byte order.
|
||||
*
|
||||
* @param v 64-bit value.
|
||||
* @param buf Destination pointer; must point to at least 8 writable bytes.
|
||||
*
|
||||
* @note
|
||||
* This function enforces a little-endian layout regardless of host endianness.
|
||||
* For hot paths you may consider an optimized version using memcpy/bswap on
|
||||
* big-endian hosts instead of byte-wise stores.
|
||||
*/
|
||||
static inline void store64_le(uint64_t v, unsigned char *buf)
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
buf[i] = static_cast<unsigned char>(v >> (8 * i));
|
||||
}
|
||||
|
||||
/**
|
||||
* @class ControlBuilder
|
||||
* @brief Helper to assemble `msghdr` and control messages for AF_ALG.
|
||||
*
|
||||
* @details
|
||||
* Builds the control payload for one `sendmsg()` call against an AF_ALG
|
||||
* skcipher operation socket:
|
||||
* - Control message #1: `ALG_SET_OP = ALG_OP_ENCRYPT`
|
||||
* - Control message #2: `ALG_SET_IV` with a 128-bit IV
|
||||
* - Data iovec: points at the plaintext buffer (here: zero-bytes of length CHUNK)
|
||||
*
|
||||
* All data structures live on the stack; constructing this helper is O(1).
|
||||
*
|
||||
* @note
|
||||
* The kernel expects `ivlen` as a host-endian u32 followed by `iv` bytes.
|
||||
* "Network order not required" is intentional for AF_ALG skcipher IVs.
|
||||
*/
|
||||
class ControlBuilder {
|
||||
public:
|
||||
/**
|
||||
* @param iv 128-bit IV (counter value), passed as 16 bytes.
|
||||
* @param plain Pointer to plaintext buffer (here: all-zero array).
|
||||
* @param len Plaintext length in bytes; determines keystream length.
|
||||
*/
|
||||
ControlBuilder(const unsigned char iv[16], void *plain, size_t len)
|
||||
{
|
||||
// ---------- Data iovec ----------
|
||||
iov_.iov_base = plain;
|
||||
iov_.iov_len = len;
|
||||
|
||||
// ---------- msghdr --------------
|
||||
msg_.msg_name = nullptr; // already bound via bind()
|
||||
msg_.msg_namelen = 0;
|
||||
msg_.msg_iov = &iov_;
|
||||
msg_.msg_iovlen = 1;
|
||||
msg_.msg_control = control_.data();
|
||||
msg_.msg_controllen = control_.size();
|
||||
msg_.msg_flags = 0;
|
||||
|
||||
// ---------- CMSG #1 : ALG_SET_OP = ENCRYPT ----------
|
||||
cmsghdr *c1 = CMSG_FIRSTHDR(&msg_);
|
||||
c1->cmsg_level = SOL_ALG;
|
||||
c1->cmsg_type = ALG_SET_OP;
|
||||
c1->cmsg_len = CMSG_LEN(sizeof(uint32_t));
|
||||
*reinterpret_cast<uint32_t*>(CMSG_DATA(c1)) = ALG_OP_ENCRYPT;
|
||||
|
||||
// ---------- CMSG #2 : ALG_SET_IV ----------
|
||||
cmsghdr *c2 = CMSG_NXTHDR(&msg_, c1);
|
||||
c2->cmsg_level = SOL_ALG;
|
||||
c2->cmsg_type = ALG_SET_IV;
|
||||
c2->cmsg_len = CMSG_LEN(sizeof(uint32_t) + 16);
|
||||
uint32_t ivlen = 16; // host endian; not network order
|
||||
std::memcpy(CMSG_DATA(c2), &ivlen, sizeof(ivlen));
|
||||
std::memcpy(CMSG_DATA(c2) + sizeof(ivlen), iv, 16);
|
||||
}
|
||||
|
||||
/// @return Prepared msghdr suitable for `sendmsg()`.
|
||||
struct msghdr *msg() { return &msg_; }
|
||||
|
||||
private:
|
||||
// Control buffer sufficient for both control messages.
|
||||
std::array<char,
|
||||
CMSG_SPACE(sizeof(uint32_t)) +
|
||||
CMSG_SPACE(sizeof(uint32_t) + 16)> control_{};
|
||||
struct msghdr msg_{};
|
||||
struct iovec iov_{};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Open a "ctr(aes)" skcipher operation socket via AF_ALG.
|
||||
*
|
||||
* @details
|
||||
* Performs the `socket()` → `bind()` → `setsockopt(ALG_SET_KEY)` → `accept()`
|
||||
* sequence. The returned fd is the operation socket used for `sendmsg()`+`read()`.
|
||||
*
|
||||
* @param key AES key (32 bytes for AES-256).
|
||||
* @return Operation socket fd (>= 0) on success, or -1 on failure.
|
||||
*
|
||||
* @warning
|
||||
* This function does not set `FD_CLOEXEC` nor handle `SIGPIPE`. Consider using
|
||||
* `SOCK_CLOEXEC` on `socket()` and `accept4()` and `MSG_NOSIGNAL` on `sendmsg()`
|
||||
* in hardened builds.
|
||||
*/
|
||||
static int open_ctr_socket(const unsigned char key[32])
|
||||
{
|
||||
// 1. Create transform socket (AF_ALG family).
|
||||
int tfm = ::socket(AF_ALG, SOCK_SEQPACKET, 0);
|
||||
if (tfm < 0) return -1;
|
||||
|
||||
// 2. Describe requested algorithm: type = "skcipher", name = "ctr(aes)".
|
||||
sockaddr_alg sa = {};
|
||||
sa.salg_family = AF_ALG;
|
||||
std::strcpy(reinterpret_cast<char*>(sa.salg_type), "skcipher");
|
||||
std::strcpy(reinterpret_cast<char*>(sa.salg_name), "ctr(aes)");
|
||||
|
||||
if (::bind(tfm, reinterpret_cast<sockaddr*>(&sa), sizeof(sa)) < 0) {
|
||||
::close(tfm); return -1;
|
||||
}
|
||||
|
||||
// 3. Upload 256-bit key.
|
||||
if (::setsockopt(tfm, SOL_ALG, ALG_SET_KEY, key, 32) < 0) {
|
||||
::close(tfm); return -1;
|
||||
}
|
||||
|
||||
// 4. Accept operation socket — the fd we will use for sendmsg/read.
|
||||
int op = ::accept(tfm, nullptr, nullptr);
|
||||
::close(tfm); // transform socket no longer needed
|
||||
return op; // may be -1 on error
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increment a 128-bit little-endian counter by @p n AES blocks.
|
||||
*
|
||||
* @details
|
||||
* The counter is represented as two 64-bit little-endian limbs in state->s[0..1].
|
||||
* The increment is performed modulo 2^128 with carry propagation from low to high.
|
||||
*
|
||||
* @param st PRNG state with s[0]=lo, s[1]=hi limbs.
|
||||
* @param n Number of AES blocks to add.
|
||||
*
|
||||
* @note
|
||||
* This is **little-endian** counter arithmetic; see the big file-level warning
|
||||
* about non-RFC CTR semantics.
|
||||
*/
|
||||
static void ctr_add(aes_ctr_state_t *st, uint64_t n)
|
||||
{
|
||||
uint64_t old = st->s[0];
|
||||
st->s[0] += n;
|
||||
if (st->s[0] < old) ++st->s[1]; // handle carry
|
||||
}
|
||||
|
||||
} // namespace (anonymous)
|
||||
|
||||
// =================================================================================================
|
||||
// PUBLIC C API IMPLEMENTATION
|
||||
// =================================================================================================
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* @brief Initialize PRNG state and lazily open the per-thread AF_ALG socket.
|
||||
*
|
||||
* @param[out] state Pointer to PRNG state (must be non-null).
|
||||
* @param[in] init_key Seed as an array of unsigned long; must provide >= 32 bytes.
|
||||
* @param[in] key_length Number of `unsigned long` words in @p init_key.
|
||||
*
|
||||
* @retval 0 Success.
|
||||
* @retval -1 Invalid parameters or AF_ALG setup failure.
|
||||
*
|
||||
* @details
|
||||
* - Zeroes the entire state and copies the first 128 bits of the seed into the
|
||||
* 128-bit counter (little-endian limb order).
|
||||
* - Saves the first 256 bits as the AES-256 key in @c global_key.
|
||||
* - Opens the AF_ALG operation socket for "ctr(aes)" on first call in this
|
||||
* thread and stores the fd in thread-local storage.
|
||||
*
|
||||
* @warning
|
||||
* The chosen IV scheme is little-endian and not RFC-interoperable.
|
||||
* Do not mix with external AES-CTR generators expecting big-endian counters.
|
||||
*/
|
||||
int aes_ctr_prng_init(aes_ctr_state_t *state,
|
||||
unsigned long init_key[],
|
||||
unsigned long key_length)
|
||||
{
|
||||
if (!state || !init_key || key_length * sizeof(unsigned long) < 32)
|
||||
return -1;
|
||||
|
||||
// Zero entire state, then put seed[0..15] into counter (LE limbs).
|
||||
std::memset(state, 0, sizeof(*state));
|
||||
std::memcpy(state->s, init_key, sizeof(uint64_t) * 2);
|
||||
|
||||
// Remember full AES-256 key (32 bytes) for possible re-opens.
|
||||
std::memcpy(global_key, init_key, 32);
|
||||
|
||||
// Open per-thread socket on first call in this thread.
|
||||
if (tls_op_fd == -1) {
|
||||
tls_op_fd = open_ctr_socket(global_key);
|
||||
if (tls_op_fd < 0) return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Produce exactly CHUNK bytes of keystream into @p bufpos.
|
||||
*
|
||||
* @param[in] state PRNG state (counter source).
|
||||
* @param[out] bufpos Destination buffer; must hold at least CHUNK bytes.
|
||||
*
|
||||
* @retval 0 Success (CHUNK bytes written).
|
||||
* @retval -1 Parameter error or syscall failure.
|
||||
*
|
||||
* @details
|
||||
* Sequence per call:
|
||||
* 1. Assemble a 128-bit IV by storing s[0] (low) and s[1] (high) as
|
||||
* little-endian 64-bit words into a 16-byte buffer.
|
||||
* 2. Build the AF_ALG control message (ALG_SET_OP=ENCRYPT, ALG_SET_IV=IV)
|
||||
* and data iovec pointing to a static all-zero plaintext of length CHUNK.
|
||||
* 3. `sendmsg()` the request and `read()` back exactly CHUNK bytes of
|
||||
* ciphertext — which, because plaintext is zero, equals the keystream.
|
||||
* 4. Increment the 128-bit counter by `BLOCKS_PER_CHUNK`.
|
||||
*
|
||||
* @note
|
||||
* The zero-plaintext buffer is static and zero-initialized once; the kernel
|
||||
* will not read uninitialized memory. Using zero plaintext is standard for
|
||||
* obtaining the raw AES-CTR keystream.
|
||||
*/
|
||||
int aes_ctr_prng_genrand_128k_to_buf(aes_ctr_state_t *state,
|
||||
unsigned char *bufpos)
|
||||
{
|
||||
if (!state || !bufpos || tls_op_fd < 0)
|
||||
return -1;
|
||||
|
||||
// --- Construct 128-bit IV from counter (little-endian limbs) -------------
|
||||
unsigned char iv[16];
|
||||
store64_le(state->s[0], iv); // little-endian low limb
|
||||
store64_le(state->s[1], iv + 8); // little-endian high limb
|
||||
|
||||
// --- Build msghdr ---------------------------------------------------------
|
||||
static unsigned char zeros[CHUNK] = {0}; // static → zero-initialised once
|
||||
ControlBuilder ctl(iv, zeros, CHUNK);
|
||||
|
||||
// --- sendmsg() + read() ---------------------------------------------------
|
||||
if (::sendmsg(tls_op_fd, ctl.msg(), 0) != (ssize_t)CHUNK) return -1;
|
||||
if (::read (tls_op_fd, bufpos, CHUNK) != (ssize_t)CHUNK) return -1;
|
||||
|
||||
// --- Advance counter ------------------------------------------------------
|
||||
ctr_add(state, BLOCKS_PER_CHUNK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Optional cleanup helper (explicitly closes the per-thread op-socket).
|
||||
*
|
||||
* @retval 0 Always succeeds.
|
||||
*
|
||||
* @details
|
||||
* The kernel will close FDs at process exit, but explicit shutdown helps
|
||||
* test harnesses and avoids keeping descriptors alive across exec().
|
||||
* Consider zeroizing @c global_key here for defense-in-depth.
|
||||
*/
|
||||
int aes_ctr_prng_shutdown(void)
|
||||
{
|
||||
if (tls_op_fd >= 0) {
|
||||
::close(tls_op_fd);
|
||||
tls_op_fd = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
60
src/aes/aes_ctr_prng.h
Normal file
60
src/aes/aes_ctr_prng.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef AES_CTR_PRNG_H
|
||||
#define AES_CTR_PRNG_H
|
||||
|
||||
/* Minimal public header for AES-256-CTR PRNG (Linux AF_ALG backend)
|
||||
*
|
||||
* Implementation detail:
|
||||
* - Uses a persistent AF_ALG "ctr(aes)" operation socket opened at init.
|
||||
* - No socket setup overhead during generation – only sendmsg + read.
|
||||
* - Thread-safety: Not safe unless externally synchronized.
|
||||
*
|
||||
* Public state remains exactly 256 bits (4×64-bit words) to allow for
|
||||
* minimalistic integration in nwipe and similar tools.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* PRNG state: exactly 256 bits (4 × 64-bit words)
|
||||
*
|
||||
* s[0] = counter low
|
||||
* s[1] = counter high
|
||||
* s[2], s[3] = reserved
|
||||
*/
|
||||
typedef struct aes_ctr_state_s {
|
||||
uint64_t s[4];
|
||||
} aes_ctr_state_t;
|
||||
|
||||
/* Initialize with >=32 bytes of seed (init_key as unsigned-long array)
|
||||
*
|
||||
* On first call, also opens the persistent AF_ALG socket.
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int aes_ctr_prng_init(aes_ctr_state_t *state,
|
||||
unsigned long init_key[],
|
||||
unsigned long key_length);
|
||||
|
||||
/* Generate one 128 KiB chunk of random data into bufpos.
|
||||
*
|
||||
* Returns 0 on success, -1 on failure.
|
||||
* Uses the persistent AF_ALG socket.
|
||||
*/
|
||||
int aes_ctr_prng_genrand_128k_to_buf(aes_ctr_state_t *state,
|
||||
unsigned char *bufpos);
|
||||
|
||||
/* Optional: Close the persistent AF_ALG socket at program shutdown.
|
||||
*
|
||||
* Not required by nwipe, but recommended for tools embedding this code.
|
||||
*/
|
||||
int aes_ctr_prng_shutdown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AES_CTR_PRNG_H */
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
* damages, or other liability, whether in an action of contract, tort, or otherwise, arising
|
||||
* from, out of, or in connection with the software or the use or other dealings in the software.
|
||||
*
|
||||
* Actually it uses subtraction as the core operation, making it conceptually closer to a
|
||||
* "Subtractive Lagged Fibonacci Generator" (SLFG) variant, but well..
|
||||
*
|
||||
* Note: This implementation is designed for non-cryptographic applications and should not be
|
||||
* used where cryptographic security is required.
|
||||
*/
|
||||
|
||||
@@ -122,6 +122,9 @@ int nwipe_conf_init()
|
||||
*/
|
||||
nwipe_conf_populate( "PDF_Certificate.PDF_Enable", "ENABLED" );
|
||||
nwipe_conf_populate( "PDF_Certificate.PDF_Preview", "DISABLED" );
|
||||
nwipe_conf_populate( "PDF_Certificate.PDF_Host_Visibility", "DISABLED" );
|
||||
nwipe_conf_populate( "PDF_Certificate.PDF_tag", "DISABLED" );
|
||||
nwipe_conf_populate( "PDF_Certificate.User_Defined_Tag", "Empty Tag" );
|
||||
|
||||
/**
|
||||
* The currently selected customer that will be printed on the report
|
||||
|
||||
@@ -59,6 +59,13 @@ typedef enum nwipe_select_t_ {
|
||||
NWIPE_SELECT_DISABLED // Do not wipe this device and do not allow it to be selected.
|
||||
} nwipe_select_t;
|
||||
|
||||
/* I/O mode for data path: auto, direct, or cached. */
|
||||
typedef enum {
|
||||
NWIPE_IO_MODE_AUTO = 0, /* Try O_DIRECT, fall back to cached I/O if not supported. */
|
||||
NWIPE_IO_MODE_DIRECT, /* Force O_DIRECT, fail if not supported. */
|
||||
NWIPE_IO_MODE_CACHED /* Force cached I/O, never attempt O_DIRECT. */
|
||||
} nwipe_io_mode_t;
|
||||
|
||||
#define NWIPE_KNOB_SPEEDRING_SIZE 30
|
||||
#define NWIPE_KNOB_SPEEDRING_GRANULARITY 10
|
||||
|
||||
@@ -82,6 +89,14 @@ typedef struct nwipe_speedring_t_
|
||||
// 20 chracters for serial number plus null Byte
|
||||
#define NWIPE_SERIALNUMBER_LENGTH 20
|
||||
|
||||
// UUID size
|
||||
#define UUID_SIZE 40
|
||||
|
||||
// Device name max size including /dev/ path
|
||||
#define DEVICE_NAME_MAX_SIZE 100
|
||||
|
||||
#define NWIPE_DEVICE_SYSFS_PATH_LENGTH 512
|
||||
|
||||
typedef struct nwipe_context_t_
|
||||
{
|
||||
/*
|
||||
@@ -99,8 +114,10 @@ typedef struct nwipe_context_t_
|
||||
int device_minor; // The minor device number.
|
||||
int device_part; // The device partition or slice number.
|
||||
char* device_name; // The device file name.
|
||||
char device_name_without_path[100];
|
||||
char gui_device_name[100];
|
||||
char device_name_without_path[DEVICE_NAME_MAX_SIZE];
|
||||
char gui_device_name[DEVICE_NAME_MAX_SIZE];
|
||||
char device_sysfs_path[NWIPE_DEVICE_SYSFS_PATH_LENGTH]; // sysfs path for topology view
|
||||
char device_name_terse[DEVICE_NAME_MAX_SIZE];
|
||||
unsigned long long device_size; // The device size in bytes.
|
||||
u64 device_size_in_sectors; // The device size in number of logical sectors, this may be 512 or 4096 sectors
|
||||
u64 device_size_in_512byte_sectors; // The device size in number of 512byte sectors, irrespective of logical sector
|
||||
@@ -117,10 +134,10 @@ typedef struct nwipe_context_t_
|
||||
int device_is_ssd; // 0 = no SSD, 1 = is a SSD
|
||||
char device_serial_no[NWIPE_SERIALNUMBER_LENGTH
|
||||
+ 1]; // Serial number(processed, 20 characters plus null termination) of the device.
|
||||
char device_UUID[UUID_SIZE]; // Only populated with the UUID if device is a partition
|
||||
int device_target; // The device target.
|
||||
|
||||
u64 eta; // The estimated number of seconds until method completion.
|
||||
int entropy_fd; // The entropy source. Usually /dev/urandom.
|
||||
int pass_count; // The number of passes performed by the working wipe method.
|
||||
u64 pass_done; // The number of bytes that have already been i/o'd in this pass.
|
||||
u64 pass_errors; // The number of errors across all passes.
|
||||
@@ -189,6 +206,7 @@ typedef struct nwipe_context_t_
|
||||
char HPA_size_text[NWIPE_DEVICE_SIZE_TXT_LENGTH]; // Human readable size bytes, KB, MB, GB ..
|
||||
int HPA_display_toggle_state; // 0 or 1 Used to toggle between "[1TB] [ 33C]" and [HDA STATUS]
|
||||
time_t HPA_toggle_time; // records a time, then if for instance 3 seconds has elapsed the display changes
|
||||
nwipe_io_mode_t io_mode; // specific I/O method for a given drive, direct or cached.
|
||||
int test_use1;
|
||||
int test_use2;
|
||||
|
||||
|
||||
44
src/cpu_features.c
Normal file
44
src/cpu_features.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "cpu_features.h"
|
||||
|
||||
/*
|
||||
* Executes the CPUID instruction and fills out the provided variables with the results.
|
||||
* eax: The function/subfunction number to query with CPUID.
|
||||
* *eax_out, *ebx_out, *ecx_out, *edx_out: Pointers to variables where the CPUID output will be stored.
|
||||
*/
|
||||
void cpuid( uint32_t eax, uint32_t* eax_out, uint32_t* ebx_out, uint32_t* ecx_out, uint32_t* edx_out )
|
||||
{
|
||||
#if defined( __i386__ ) || defined( __x86_64__ ) /* only on x86 */
|
||||
#if defined( _MSC_VER ) /* MSVC */
|
||||
int r[4];
|
||||
__cpuid( r, eax );
|
||||
*eax_out = r[0];
|
||||
*ebx_out = r[1];
|
||||
*ecx_out = r[2];
|
||||
*edx_out = r[3];
|
||||
#elif defined( __GNUC__ ) /* GCC/Clang */
|
||||
__asm__ __volatile__( "cpuid"
|
||||
: "=a"( *eax_out ), "=b"( *ebx_out ), "=c"( *ecx_out ), "=d"( *edx_out )
|
||||
: "a"( eax ) );
|
||||
#else
|
||||
#error "Unsupported compiler"
|
||||
#endif
|
||||
#else /* not-x86 */
|
||||
(void) eax;
|
||||
*eax_out = *ebx_out = *ecx_out = *edx_out = 0; /* CPUID = 0 */
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the AES-NI instruction set is supported by the processor.
|
||||
* Returns 1 (true) if supported, 0 (false) otherwise.
|
||||
*/
|
||||
int has_aes_ni( void )
|
||||
{
|
||||
#if defined( __i386__ ) || defined( __x86_64__ ) /* only for x86 */
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
cpuid( 1, &eax, &ebx, &ecx, &edx );
|
||||
return ( ecx & ( 1u << 25 ) ) != 0; /* Bit 25 = AES-NI */
|
||||
#else /* ARM, RISC-V … */
|
||||
return 0; /* no AES-NI */
|
||||
#endif
|
||||
}
|
||||
18
src/cpu_features.h
Normal file
18
src/cpu_features.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef NWIPE_CPU_FEATURES_H
|
||||
#define NWIPE_CPU_FEATURES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void cpuid( uint32_t eax, uint32_t* eax_out, uint32_t* ebx_out, uint32_t* ecx_out, uint32_t* edx_out );
|
||||
|
||||
int has_aes_ni( void );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NWIPE_CPU_FEATURES_H */
|
||||
125
src/create_pdf.c
125
src/create_pdf.c
@@ -50,10 +50,12 @@
|
||||
struct pdf_doc* pdf;
|
||||
struct pdf_object* page;
|
||||
|
||||
char model_header[50] = ""; /* Model text in the header */
|
||||
char serial_header[30] = ""; /* Serial number text in the header */
|
||||
char model_header[55] = ""; /* Model text in the header */
|
||||
char serial_header[35] = ""; /* Serial number text in the header */
|
||||
char hostid_header[DMIDECODE_RESULT_LENGTH + 15] = ""; /* host identification, UUID, serial number, system tag */
|
||||
char barcode[100] = ""; /* Contents of the barcode, i.e model:serial */
|
||||
char pdf_footer[MAX_PDF_FOOTER_TEXT_LENGTH];
|
||||
char tag_header[MAX_PDF_TAG_LENGTH];
|
||||
float height;
|
||||
float page_width;
|
||||
int status_icon;
|
||||
@@ -129,26 +131,20 @@ int create_pdf( nwipe_context_t* ptr )
|
||||
/* Obtain page page_width */
|
||||
page_width = pdf_page_width( page_1 );
|
||||
|
||||
/**********************************************
|
||||
* Initialise serial no. to unknown if empty
|
||||
*/
|
||||
if( c->device_serial_no[0] == 0 )
|
||||
{
|
||||
snprintf( c->device_serial_no, sizeof( c->device_serial_no ), "Unknown" );
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Create header and footer on page 1, with the exception of the green
|
||||
* tick/red icon which is set from the 'status' section below
|
||||
*/
|
||||
pdf_add_text_wrap( pdf, NULL, pdf_footer, 12, 0, 30, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
pdf_add_line( pdf, NULL, 50, 50, 550, 50, 3, PDF_BLACK );
|
||||
pdf_add_line( pdf, NULL, 50, 650, 550, 650, 3, PDF_BLACK );
|
||||
pdf_add_image_data( pdf, NULL, 45, 665, 100, 100, bin2c_shred_db_jpg, 27063 );
|
||||
pdf_set_font( pdf, "Helvetica-Bold" );
|
||||
snprintf( model_header, sizeof( model_header ), " %s: %s ", "Model", c->device_model );
|
||||
pdf_add_text_wrap( pdf, NULL, model_header, 14, 0, 755, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
snprintf( serial_header, sizeof( serial_header ), " %s: %s ", "S/N", c->device_serial_no );
|
||||
pdf_add_text_wrap( pdf, NULL, serial_header, 14, 0, 735, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
pdf_set_font( pdf, "Helvetica" );
|
||||
|
||||
pdf_add_text_wrap( pdf, NULL, "Disk Erasure Report", 24, 0, 695, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
snprintf( barcode, sizeof( barcode ), "%s:%s", c->device_model, c->device_serial_no );
|
||||
pdf_add_text_wrap(
|
||||
pdf, NULL, "Page 1 - Erasure Status", 14, 0, 670, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
pdf_add_barcode( pdf, NULL, PDF_BARCODE_128A, 100, 790, 400, 25, barcode, PDF_BLACK );
|
||||
pdf_header_footer_text( c, "Page 1 - Erasure Status" );
|
||||
|
||||
/* ------------------------ */
|
||||
/* Organisation Information */
|
||||
@@ -243,10 +239,6 @@ int create_pdf( nwipe_context_t* ptr )
|
||||
* Serial no.
|
||||
*/
|
||||
pdf_add_text( pdf, NULL, "Serial:", 12, 340, 410, PDF_GRAY );
|
||||
if( c->device_serial_no[0] == 0 )
|
||||
{
|
||||
snprintf( c->device_serial_no, sizeof( c->device_serial_no ), "Unknown" );
|
||||
}
|
||||
pdf_set_font( pdf, "Helvetica-Bold" );
|
||||
pdf_add_text( pdf, NULL, c->device_serial_no, text_size_data, 380, 410, PDF_BLACK );
|
||||
pdf_set_font( pdf, "Helvetica" );
|
||||
@@ -801,18 +793,19 @@ int create_pdf( nwipe_context_t* ptr )
|
||||
* Create the reports filename
|
||||
*
|
||||
* Sanitize the strings that we are going to use to create the report filename
|
||||
* by converting any non alphanumeric characters to an underscore or hyphon
|
||||
* by converting any non alphanumeric characters to an underscore or hyphen
|
||||
*/
|
||||
replace_non_alphanumeric( end_time_text, '-' );
|
||||
replace_non_alphanumeric( c->device_model, '_' );
|
||||
replace_non_alphanumeric( c->device_serial_no, '_' );
|
||||
snprintf( c->PDF_filename,
|
||||
sizeof( c->PDF_filename ),
|
||||
"%s/nwipe_report_%s_Model_%s_Serial_%s.pdf",
|
||||
"%s/nwipe_report_%s_Model_%s_Serial_%s_device_%s.pdf",
|
||||
nwipe_options.PDFreportpath,
|
||||
end_time_text,
|
||||
c->device_model,
|
||||
c->device_serial_no );
|
||||
c->device_serial_no,
|
||||
c->device_name_terse );
|
||||
|
||||
pdf_save( pdf, c->PDF_filename );
|
||||
pdf_destroy( pdf );
|
||||
@@ -974,21 +967,7 @@ void create_header_and_footer( nwipe_context_t* c, char* page_title )
|
||||
* Create header and footer on most recently added page, with the exception
|
||||
* of the green tick/red icon which is set from the 'status' section below.
|
||||
*/
|
||||
pdf_add_text_wrap( pdf, NULL, pdf_footer, 12, 0, 30, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
pdf_add_line( pdf, NULL, 50, 50, 550, 50, 3, PDF_BLACK );
|
||||
pdf_add_line( pdf, NULL, 50, 650, 550, 650, 3, PDF_BLACK );
|
||||
pdf_add_image_data( pdf, NULL, 45, 665, 100, 100, bin2c_shred_db_jpg, 27063 );
|
||||
pdf_set_font( pdf, "Helvetica-Bold" );
|
||||
snprintf( model_header, sizeof( model_header ), " %s: %s ", "Model", c->device_model );
|
||||
pdf_add_text_wrap( pdf, NULL, model_header, 14, 0, 755, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
snprintf( serial_header, sizeof( serial_header ), " %s: %s ", "S/N", c->device_serial_no );
|
||||
pdf_add_text_wrap( pdf, NULL, serial_header, 14, 0, 735, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
pdf_set_font( pdf, "Helvetica" );
|
||||
|
||||
pdf_add_text_wrap( pdf, NULL, "Disk Erasure Report", 24, 0, 695, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
snprintf( barcode, sizeof( barcode ), "%s:%s", c->device_model, c->device_serial_no );
|
||||
pdf_add_text_wrap( pdf, NULL, page_title, 14, 0, 670, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
pdf_add_barcode( pdf, NULL, PDF_BARCODE_128A, 100, 790, 400, 25, barcode, PDF_BLACK );
|
||||
pdf_header_footer_text( c, page_title );
|
||||
|
||||
/**********************************************************
|
||||
* Display the appropriate status icon, top right on page on
|
||||
@@ -1019,3 +998,71 @@ void create_header_and_footer( nwipe_context_t* c, char* page_title )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pdf_header_footer_text( nwipe_context_t* c, char* page_title )
|
||||
{
|
||||
extern char dmidecode_system_serial_number[DMIDECODE_RESULT_LENGTH];
|
||||
extern char dmidecode_system_uuid[DMIDECODE_RESULT_LENGTH];
|
||||
|
||||
const char* user_defined_tag;
|
||||
|
||||
/* variables used by libconfig for extracting data from nwipe.conf */
|
||||
config_setting_t* setting;
|
||||
extern config_t nwipe_cfg;
|
||||
|
||||
pdf_add_text_wrap( pdf, NULL, pdf_footer, 12, 0, 30, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
pdf_add_line( pdf, NULL, 50, 50, 550, 50, 3, PDF_BLACK ); // Footer full width Line
|
||||
pdf_add_line( pdf, NULL, 50, 650, 550, 650, 3, PDF_BLACK ); // Header full width Line
|
||||
pdf_add_line( pdf, NULL, 175, 734, 425, 734, 3, PDF_BLACK ); // Header Page number, disk model divider line
|
||||
pdf_add_image_data( pdf, NULL, 45, 665, 100, 100, bin2c_shred_db_jpg, 27063 );
|
||||
pdf_set_font( pdf, "Helvetica-Bold" );
|
||||
|
||||
if( nwipe_options.PDFtag || nwipe_options.PDF_toggle_host_info )
|
||||
{
|
||||
snprintf( model_header, sizeof( model_header ), " %s: %s ", "Disk Model", c->device_model );
|
||||
pdf_add_text_wrap( pdf, NULL, model_header, 11, 0, 718, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
snprintf( serial_header, sizeof( serial_header ), " %s: %s ", "Disk S/N", c->device_serial_no );
|
||||
pdf_add_text_wrap( pdf, NULL, serial_header, 11, 0, 703, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
|
||||
/* Display host UUID & S/N is host visibility is enabled in PDF */
|
||||
if( nwipe_options.PDF_toggle_host_info )
|
||||
{
|
||||
snprintf(
|
||||
hostid_header, sizeof( hostid_header ), " %s: %s ", "System S/N", dmidecode_system_serial_number );
|
||||
pdf_add_text_wrap( pdf, NULL, hostid_header, 11, 0, 688, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
snprintf( hostid_header, sizeof( hostid_header ), " %s: %s ", "System uuid", dmidecode_system_uuid );
|
||||
pdf_add_text_wrap( pdf, NULL, hostid_header, 11, 0, 673, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
}
|
||||
|
||||
/* libconfig: Obtain PDF_Certificate.User_Defined_Tag from nwipe.conf */
|
||||
setting = config_lookup( &nwipe_cfg, "PDF_Certificate" );
|
||||
|
||||
if( config_setting_lookup_string( setting, "User_Defined_Tag", &user_defined_tag ) )
|
||||
{
|
||||
if( user_defined_tag[0] != 0 )
|
||||
{
|
||||
snprintf( tag_header, sizeof( tag_header ), " %s: %s ", "Tag", user_defined_tag );
|
||||
pdf_add_text_wrap(
|
||||
pdf, NULL, tag_header, 11, 0, 658, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( tag_header, sizeof( tag_header ), " %s: %s ", "Tag", "libconfig:tag error" );
|
||||
pdf_add_text_wrap( pdf, NULL, tag_header, 11, 0, 658, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf( model_header, sizeof( model_header ), " %s: %s ", "Disk Model", c->device_model );
|
||||
pdf_add_text_wrap( pdf, NULL, model_header, 11, 0, 696, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
snprintf( serial_header, sizeof( serial_header ), " %s: %s ", "Disk S/N", c->device_serial_no );
|
||||
pdf_add_text_wrap( pdf, NULL, serial_header, 11, 0, 681, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
}
|
||||
pdf_set_font( pdf, "Helvetica" );
|
||||
|
||||
pdf_add_text_wrap( pdf, NULL, "Disk Erasure Report", 24, 0, 765, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
snprintf( barcode, sizeof( barcode ), "%s:%s", c->device_model, c->device_serial_no );
|
||||
pdf_add_text_wrap( pdf, NULL, page_title, 14, 0, 745, PDF_BLACK, page_width, PDF_ALIGN_CENTER, &height );
|
||||
pdf_add_barcode( pdf, NULL, PDF_BARCODE_128A, 100, 790, 400, 25, barcode, PDF_BLACK );
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#define CREATE_PDF_H_
|
||||
|
||||
#define MAX_PDF_FOOTER_TEXT_LENGTH 100
|
||||
#define MAX_PDF_TAG_LENGTH 40
|
||||
|
||||
#define STATUS_ICON_GREEN_TICK 1
|
||||
#define STATUS_ICON_YELLOW_EXCLAMATION 2
|
||||
@@ -49,4 +50,6 @@ int nwipe_get_smart_data( nwipe_context_t* );
|
||||
|
||||
void create_header_and_footer( nwipe_context_t*, char* );
|
||||
|
||||
void pdf_header_footer_text( nwipe_context_t*, char* );
|
||||
|
||||
#endif /* CREATE_PDF_H_ */
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
void customer_processes( int );
|
||||
void select_customers( int, char** );
|
||||
void delete_customer();
|
||||
void delete_customer( int, char** );
|
||||
void add_customer();
|
||||
void write_customer_csv_entry( char*, char*, char*, char* );
|
||||
void delete_customer_csv_entry( int* );
|
||||
|
||||
253
src/device.c
253
src/device.c
@@ -49,6 +49,120 @@
|
||||
|
||||
int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount );
|
||||
char* trim( char* str );
|
||||
static void nwipe_normalize_serial( char* serial );
|
||||
|
||||
/*
|
||||
* Resolve a device path (including /dev/disk/by-* symlinks) to its
|
||||
* underlying block device id (dev_t).
|
||||
*
|
||||
* Returns 0 on success and fills *out_rdev.
|
||||
* Returns -1 on error or if the path is not a block device.
|
||||
*/
|
||||
static int nwipe_path_to_rdev( const char* path, dev_t* out_rdev )
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if( path == NULL || out_rdev == NULL )
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* stat() follows symlinks by default, which is what we want for
|
||||
* persistent names in /dev/disk/by-id, /dev/disk/by-path, etc.
|
||||
*/
|
||||
if( stat( path, &st ) != 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( !S_ISBLK( st.st_mode ) )
|
||||
{
|
||||
/* Not a block device node. */
|
||||
errno = ENOTBLK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out_rdev = st.st_rdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a candidate device node should be excluded based on the
|
||||
* --exclude list. Matching is done primarily by device identity
|
||||
* (major/minor via st_rdev), so persistent names like /dev/disk/by-id/*
|
||||
* are safe. We keep legacy string-based matching as a fallback.
|
||||
*
|
||||
* Returns 1 if the candidate should be excluded, 0 otherwise.
|
||||
*/
|
||||
static int nwipe_is_excluded_device( const char* candidate_devnode )
|
||||
{
|
||||
dev_t cand_rdev;
|
||||
int have_cand_rdev;
|
||||
int i;
|
||||
|
||||
/* Try to resolve the candidate device to a dev_t. */
|
||||
have_cand_rdev = ( nwipe_path_to_rdev( candidate_devnode, &cand_rdev ) == 0 );
|
||||
|
||||
for( i = 0; i < MAX_NUMBER_EXCLUDED_DRIVES; i++ )
|
||||
{
|
||||
const char* ex = nwipe_options.exclude[i];
|
||||
dev_t ex_rdev;
|
||||
int have_ex_rdev;
|
||||
const char* base;
|
||||
|
||||
/* Empty slot in the exclude array. */
|
||||
if( ex == NULL || ex[0] == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* First try: both candidate and exclude entry resolve to block
|
||||
* devices; compare device ids (major/minor).
|
||||
*/
|
||||
have_ex_rdev = ( nwipe_path_to_rdev( ex, &ex_rdev ) == 0 );
|
||||
if( have_cand_rdev && have_ex_rdev && ex_rdev == cand_rdev )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", candidate_devnode );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fallback 1: exact string match. This keeps compatibility with
|
||||
* older usage like --exclude=/dev/sda or --exclude=/dev/mapper/cryptswap1.
|
||||
*/
|
||||
if( strcmp( candidate_devnode, ex ) == 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", candidate_devnode );
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fallback 2: match against the basename only, so that an
|
||||
* exclude entry like "sda" still works even if the full path is
|
||||
* /dev/sda.
|
||||
*/
|
||||
base = strrchr( candidate_devnode, '/' );
|
||||
if( base != NULL )
|
||||
{
|
||||
base++;
|
||||
}
|
||||
else
|
||||
{
|
||||
base = candidate_devnode;
|
||||
}
|
||||
|
||||
if( strcmp( base, ex ) == 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", candidate_devnode );
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int terminate_signal;
|
||||
|
||||
@@ -138,15 +252,11 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
|
||||
|
||||
bus = 0;
|
||||
|
||||
/* Check whether this drive is on the excluded drive list ? */
|
||||
idx = 0;
|
||||
while( idx < MAX_NUMBER_EXCLUDED_DRIVES )
|
||||
/* Check whether this drive is on the excluded drive list. */
|
||||
if( nwipe_is_excluded_device( dev->path ) )
|
||||
{
|
||||
if( !strcmp( dev->path, nwipe_options.exclude[idx++] ) )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Device %s excluded as per command line option -e", dev->path );
|
||||
return 0;
|
||||
}
|
||||
/* Already logged inside nwipe_is_excluded_device(). */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether the user has specified using the --nousb option
|
||||
@@ -156,7 +266,7 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
|
||||
if( nwipe_options.nousb )
|
||||
{
|
||||
/* retrieve bus and drive serial number, HOWEVER we are only interested in the bus at this time */
|
||||
r = nwipe_get_device_bus_type_and_serialno( dev->path, &bus, &is_ssd, tmp_serial );
|
||||
r = nwipe_get_device_bus_type_and_serialno( dev->path, &bus, &is_ssd, tmp_serial, NULL, 0 );
|
||||
|
||||
/* See nwipe_get_device_bus_type_and_serialno() function for meaning of these codes */
|
||||
if( r == 0 || ( r >= 3 && r <= 6 ) )
|
||||
@@ -216,6 +326,19 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
|
||||
/* remove /dev/ from device, right justify and prefix name so string length is eight characters */
|
||||
nwipe_strip_path( next_device->device_name_without_path, next_device->device_name );
|
||||
|
||||
const char* device_name_terse;
|
||||
device_name_terse = skip_whitespace( next_device->device_name_without_path );
|
||||
if( device_name_terse != NULL )
|
||||
{
|
||||
/* remove the leading whitespace and save result, we use the device without path and no leading or trailing
|
||||
* space in pdf file creation later */
|
||||
strcpy( next_device->device_name_terse, device_name_terse );
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy( next_device->device_name_terse, "_" );
|
||||
}
|
||||
|
||||
/* To maintain column alignment in the gui we have to remove /dev/ from device names that
|
||||
* exceed eight characters including the /dev/ path.
|
||||
*/
|
||||
@@ -238,22 +361,26 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
|
||||
next_device->device_size_text = next_device->device_size_txt;
|
||||
next_device->result = -2;
|
||||
|
||||
/* Attempt to get serial number of device.
|
||||
*/
|
||||
next_device->device_serial_no[0] = 0; /* initialise the serial number */
|
||||
/* Attempt to get serial number of device. */
|
||||
next_device->device_serial_no[0] = '\0'; /* initialise the serial number */
|
||||
|
||||
if( ( fd = open( next_device->device_name = dev->path, O_RDONLY ) ) == ERR )
|
||||
fd = open( next_device->device_name = dev->path, O_RDONLY );
|
||||
if( fd == ERR )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_WARNING, "Unable to open device %s to obtain serial number", next_device->device_name );
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't check the ioctl return status because there are plenty of situations where a serial number may not be
|
||||
* returned by ioctl such as USB drives, logical volumes, encryted volumes, so the log file would have multiple
|
||||
* benign ioctl errors reported which isn't necessarily a problem.
|
||||
*/
|
||||
ioctl( fd, HDIO_GET_IDENTITY, &next_device->identity );
|
||||
close( fd );
|
||||
else
|
||||
{
|
||||
/*
|
||||
* We don't check the ioctl return status because there are plenty of
|
||||
* situations where a serial number may not be returned by ioctl such as
|
||||
* USB drives, logical volumes, encrypted volumes, so the log file
|
||||
* would have multiple benign ioctl errors reported which isn't
|
||||
* necessarily a problem.
|
||||
*/
|
||||
ioctl( fd, HDIO_GET_IDENTITY, &next_device->identity );
|
||||
close( fd );
|
||||
}
|
||||
|
||||
for( idx = 0; idx < NWIPE_SERIALNUMBER_LENGTH; idx++ )
|
||||
{
|
||||
@@ -274,8 +401,12 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
|
||||
trim( (char*) next_device->device_serial_no );
|
||||
|
||||
/* if we couldn't obtain serial number by using the above method .. try this */
|
||||
r = nwipe_get_device_bus_type_and_serialno(
|
||||
next_device->device_name, &next_device->device_type, &next_device->device_is_ssd, tmp_serial );
|
||||
r = nwipe_get_device_bus_type_and_serialno( next_device->device_name,
|
||||
&next_device->device_type,
|
||||
&next_device->device_is_ssd,
|
||||
tmp_serial,
|
||||
next_device->device_sysfs_path,
|
||||
sizeof( next_device->device_sysfs_path ) );
|
||||
|
||||
/* If serial number & bus retrieved (0) OR unsupported USB bus identified (5) */
|
||||
if( r == 0 || r == 5 )
|
||||
@@ -303,6 +434,9 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
|
||||
* of those strings we should explicitly terminate the string */
|
||||
next_device->device_serial_no[NWIPE_SERIALNUMBER_LENGTH] = 0;
|
||||
|
||||
/* Ensure the serial number cannot break the ncurses UI. */
|
||||
nwipe_normalize_serial( next_device->device_serial_no );
|
||||
|
||||
/* Initialise the variables that toggle the [size][temp c] with [HPA status]
|
||||
* Not currently used, but may be used in the future or for other purposes
|
||||
*/
|
||||
@@ -428,6 +562,20 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount )
|
||||
hpa_dco_status( next_device );
|
||||
}
|
||||
|
||||
/*************************************
|
||||
* Check whether the device has a UUID
|
||||
*/
|
||||
char uuid[UUID_SIZE] = "";
|
||||
if( get_device_uuid( next_device->device_name, uuid ) == 0 )
|
||||
{
|
||||
strncpy( next_device->device_UUID, uuid, UUID_SIZE );
|
||||
nwipe_log( NWIPE_LOG_INFO, "UUID for %s is: %s\n", next_device->device_name, next_device->device_UUID );
|
||||
}
|
||||
else
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_INFO, "No UUID available for %s\n", next_device->device_name );
|
||||
}
|
||||
|
||||
/* print an empty line to separate the drives in the log */
|
||||
nwipe_log( NWIPE_LOG_INFO, " " );
|
||||
|
||||
@@ -488,7 +636,46 @@ char* trim( char* str )
|
||||
return str;
|
||||
}
|
||||
|
||||
int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, int* is_ssd, char* serialnumber )
|
||||
/*
|
||||
* Remove non-ASCII and control characters from a serial number string,
|
||||
* then trim leading/trailing whitespace and left-justify it in-place.
|
||||
* This keeps the value safe for ncurses output.
|
||||
*/
|
||||
static void nwipe_normalize_serial( char* serial )
|
||||
{
|
||||
unsigned char ch;
|
||||
char* src;
|
||||
char* dst;
|
||||
|
||||
if( serial == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
src = dst = serial;
|
||||
|
||||
while( ( ch = (unsigned char) *src++ ) != '\0' )
|
||||
{
|
||||
if( isascii( ch ) && !iscntrl( ch ) )
|
||||
{
|
||||
*dst++ = (char) ch;
|
||||
}
|
||||
/* Alle remaining control characters will be dropped ( >0x7F) */
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
|
||||
/* Use existing trim() function */
|
||||
trim( serial );
|
||||
}
|
||||
|
||||
int nwipe_get_device_bus_type_and_serialno( char* device,
|
||||
nwipe_device_t* bus,
|
||||
int* is_ssd,
|
||||
char* serialnumber,
|
||||
char* sysfs_path,
|
||||
size_t sysfs_path_size )
|
||||
|
||||
{
|
||||
/* The caller provides a string that contains the device, i.e. /dev/sdc, also a pointer
|
||||
* to an integer (bus type), another pointer to an integer (is_ssd), and finally a 21 byte
|
||||
@@ -535,6 +722,14 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, i
|
||||
"serial number:", "lu wwn device id:", "logical unit id:", "" /* Don't remove this empty string !, important */
|
||||
};
|
||||
|
||||
/* Ensure the serialnumber buffer is in a defined state even if we
|
||||
* never find a "serial number:" line in smartctl output.
|
||||
*/
|
||||
if( serialnumber != NULL )
|
||||
{
|
||||
memset( serialnumber, 0, NWIPE_SERIALNUMBER_LENGTH + 1 );
|
||||
}
|
||||
|
||||
/* Initialise return value */
|
||||
set_return_value = 0;
|
||||
|
||||
@@ -619,11 +814,19 @@ int nwipe_get_device_bus_type_and_serialno( char* device, nwipe_device_t* bus, i
|
||||
if( fp != NULL )
|
||||
{
|
||||
/* Read the output a line at a time - output it. */
|
||||
|
||||
if( fgets( result, sizeof( result ) - 1, fp ) != NULL )
|
||||
{
|
||||
strip_CR_LF( result );
|
||||
|
||||
if( sysfs_path != NULL && sysfs_path_size > 0 )
|
||||
{
|
||||
strncpy( sysfs_path, result, sysfs_path_size - 1 );
|
||||
sysfs_path[sysfs_path_size - 1] = '\0';
|
||||
}
|
||||
|
||||
if( nwipe_options.verbose )
|
||||
{
|
||||
strip_CR_LF( result );
|
||||
nwipe_log( NWIPE_LOG_DEBUG, "Readlink: %s", result );
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,12 @@ int nwipe_device_scan( nwipe_context_t*** c ); // Find devices that we can wipe
|
||||
*/
|
||||
int nwipe_device_get( nwipe_context_t*** c, char** devnamelist, int ndevnames ); // Get info about devices to wipe.
|
||||
|
||||
int nwipe_get_device_bus_type_and_serialno( char*, nwipe_device_t*, int*, char* );
|
||||
int nwipe_get_device_bus_type_and_serialno( char* device,
|
||||
nwipe_device_t* bus,
|
||||
int* is_ssd,
|
||||
char* serialnumber,
|
||||
char* sysfs_path,
|
||||
size_t sysfs_path_size );
|
||||
void strip_CR_LF( char* );
|
||||
void determine_disk_capacity_nomenclature( u64, char* );
|
||||
void remove_ATA_prefix( char* );
|
||||
|
||||
@@ -67,6 +67,10 @@ void nwipe_gui_add_customer_address( char* ); // Add new customer address
|
||||
void nwipe_gui_add_customer_contact_name( char* ); // Add new customer contact name
|
||||
void nwipe_gui_add_customer_contact_phone( char* ); // Add new customer contact phone
|
||||
int nwipe_gui_yes_no_footer( void ); // Change footer to yes no
|
||||
void nwipe_gui_user_defined_tag( void ); // Edit user defined text to be added to pdf report
|
||||
void nwipe_gui_pdf_certificate_edit_user_defined_tag( const char* );
|
||||
void nwipe_gui_view_device( int count, nwipe_context_t** c, int focus );
|
||||
void nwipe_gui_benchmark_prng( void );
|
||||
|
||||
/** nwipe_gui_preview_org_customer( int )
|
||||
* Display a editable preview of organisation, customer and date/time
|
||||
|
||||
@@ -25,8 +25,7 @@ MODIFIED:
|
||||
*(r++) = b = ind(mm,y>>RANDSIZL) + x; \
|
||||
}
|
||||
|
||||
void isaac(ctx)
|
||||
randctx *ctx;
|
||||
void isaac(randctx *ctx)
|
||||
{
|
||||
register ub4 a,b,x,y,*m,*mm,*m2,*r,*mend;
|
||||
mm=ctx->randmem;
|
||||
@@ -64,9 +63,7 @@ randctx *ctx;
|
||||
}
|
||||
|
||||
/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
|
||||
void randinit(ctx, flag)
|
||||
randctx *ctx;
|
||||
word flag;
|
||||
void randinit(randctx *ctx, word flag)
|
||||
{
|
||||
word i;
|
||||
ub4 a,b,c,d,e,f,g,h;
|
||||
|
||||
@@ -32,10 +32,9 @@ typedef struct randctx randctx;
|
||||
If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
void randinit(/*_ randctx *r, word flag _*/);
|
||||
|
||||
void isaac(/*_ randctx *r _*/);
|
||||
void randinit(randctx *r, word flag);
|
||||
|
||||
void isaac(randctx *r);
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
@@ -507,6 +507,11 @@ void nwipe_log_OSinfo()
|
||||
return;
|
||||
}
|
||||
|
||||
/* Globally accessable dmidecode host identifiable data. */
|
||||
char dmidecode_system_serial_number[DMIDECODE_RESULT_LENGTH];
|
||||
char dmidecode_system_uuid[DMIDECODE_RESULT_LENGTH];
|
||||
char dmidecode_baseboard_serial_number[DMIDECODE_RESULT_LENGTH];
|
||||
|
||||
int nwipe_log_sysinfo()
|
||||
{
|
||||
FILE* fp;
|
||||
@@ -559,9 +564,16 @@ int nwipe_log_sysinfo()
|
||||
char cmd[sizeof( dmidecode_keywords ) + sizeof( dmidecode_command2 )];
|
||||
|
||||
unsigned int keywords_idx;
|
||||
unsigned int i;
|
||||
|
||||
keywords_idx = 0;
|
||||
|
||||
/* Initialise every character in the string with zero */
|
||||
for( i = 0; i < DMIDECODE_RESULT_LENGTH; i++ )
|
||||
{
|
||||
dmidecode_system_serial_number[i] = 0;
|
||||
}
|
||||
|
||||
p_dmidecode_command = 0;
|
||||
|
||||
if( system( "which dmidecode > /dev/null 2>&1" ) )
|
||||
@@ -619,11 +631,35 @@ int nwipe_log_sysinfo()
|
||||
else
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_INFO, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
|
||||
|
||||
/* if system-serial-number copy result to extern string */
|
||||
if( keywords_idx == 5 )
|
||||
{
|
||||
strncpy( dmidecode_system_serial_number, path, DMIDECODE_RESULT_LENGTH );
|
||||
dmidecode_system_serial_number[DMIDECODE_RESULT_LENGTH - 1] = 0;
|
||||
}
|
||||
if( keywords_idx == 6 )
|
||||
{
|
||||
strncpy( dmidecode_system_uuid, path, DMIDECODE_RESULT_LENGTH );
|
||||
dmidecode_system_uuid[DMIDECODE_RESULT_LENGTH - 1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_INFO, "%s = %s", &dmidecode_keywords[keywords_idx][0][0], path );
|
||||
|
||||
/* if system-serial-number copy result to extern string */
|
||||
if( keywords_idx == 5 )
|
||||
{
|
||||
strncpy( dmidecode_system_serial_number, path, DMIDECODE_RESULT_LENGTH );
|
||||
dmidecode_system_serial_number[DMIDECODE_RESULT_LENGTH - 1] = 0;
|
||||
}
|
||||
if( keywords_idx == 6 )
|
||||
{
|
||||
strncpy( dmidecode_system_uuid, path, DMIDECODE_RESULT_LENGTH );
|
||||
dmidecode_system_uuid[DMIDECODE_RESULT_LENGTH - 1] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* close */
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#define OS_info_Line_offset 31 /* OS_info line offset in log */
|
||||
#define OS_info_Line_Length 48 /* OS_info line length */
|
||||
|
||||
#define DMIDECODE_RESULT_LENGTH 64
|
||||
|
||||
typedef enum nwipe_log_t_ {
|
||||
NWIPE_LOG_NONE = 0,
|
||||
NWIPE_LOG_DEBUG, // Output only when --verbose option used on cmd line.
|
||||
|
||||
152
src/method.c
152
src/method.c
@@ -47,6 +47,52 @@
|
||||
#include "options.h"
|
||||
#include "pass.h"
|
||||
#include "logging.h"
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h> /* SYS_getrandom */
|
||||
#if defined( __linux__ )
|
||||
/* On glibc/musl with <sys/random.h> available, it's fine (optional). */
|
||||
/* #include <sys/random.h> */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Fill a buffer with cryptographically secure random bytes using getrandom(2).
|
||||
*
|
||||
* This wrapper blocks until the kernel CRNG is initialized, then loops until
|
||||
* @p len bytes are written (handling short reads and EINTR/EAGAIN).
|
||||
*
|
||||
* @param[out] buf Destination buffer.
|
||||
* @param[in] len Number of bytes to generate.
|
||||
* @return On success, returns (ssize_t)len.
|
||||
* On error, returns -errno and leaves errno set.
|
||||
*/
|
||||
static ssize_t nwipe_read_entropy( void* buf, size_t len )
|
||||
{
|
||||
unsigned char* p = (unsigned char*) buf;
|
||||
size_t n = len;
|
||||
|
||||
while( n > 0 )
|
||||
{
|
||||
/* Prefer the raw syscall to avoid libc version pitfalls. */
|
||||
ssize_t r = syscall( SYS_getrandom, p, n, 0 /* blocking */ );
|
||||
if( r < 0 )
|
||||
{
|
||||
if( errno == EINTR || errno == EAGAIN )
|
||||
{
|
||||
continue; /* retry */
|
||||
}
|
||||
return -errno;
|
||||
}
|
||||
if( r == 0 )
|
||||
{
|
||||
/* Extremely unlikely: treat as transient and retry. */
|
||||
continue;
|
||||
}
|
||||
p += r;
|
||||
n -= (size_t) r;
|
||||
}
|
||||
return (ssize_t) len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Comment Legend
|
||||
@@ -68,6 +114,8 @@ const char* nwipe_one_label = "Fill With Ones";
|
||||
const char* nwipe_verify_zero_label = "Verify Zeros (0x00)";
|
||||
const char* nwipe_verify_one_label = "Verify Ones (0xFF)";
|
||||
const char* nwipe_is5enh_label = "HMG IS5 Enhanced";
|
||||
const char* nwipe_bruce7_label = "Bruce Schneier 7-Pass";
|
||||
const char* nwipe_bmb_label = "BMB21-2019";
|
||||
|
||||
const char* nwipe_unknown_label = "Unknown Method (FIXME)";
|
||||
|
||||
@@ -118,6 +166,14 @@ const char* nwipe_method_label( void* method )
|
||||
{
|
||||
return nwipe_is5enh_label;
|
||||
}
|
||||
if( method == &nwipe_bruce7 )
|
||||
{
|
||||
return nwipe_bruce7_label;
|
||||
}
|
||||
if( method == &nwipe_bmb )
|
||||
{
|
||||
return nwipe_bmb_label;
|
||||
}
|
||||
|
||||
/* else */
|
||||
return nwipe_unknown_label;
|
||||
@@ -282,7 +338,7 @@ void* nwipe_dod522022m( void* ptr )
|
||||
{ 0, NULL } };
|
||||
|
||||
/* Load the array with random characters. */
|
||||
r = read( c->entropy_fd, &dod, sizeof( dod ) );
|
||||
r = nwipe_read_entropy( &dod, sizeof( dod ) );
|
||||
|
||||
/* NOTE: Only the random data in dod[0], dod[3], and dod[4] is actually used. */
|
||||
|
||||
@@ -353,7 +409,7 @@ void* nwipe_dodshort( void* ptr )
|
||||
{ 0, NULL } };
|
||||
|
||||
/* Load the array with random characters. */
|
||||
r = read( c->entropy_fd, &dod, sizeof( dod ) );
|
||||
r = nwipe_read_entropy( &dod, sizeof( dod ) );
|
||||
|
||||
/* NOTE: Only the random data in dod[0] is actually used. */
|
||||
|
||||
@@ -453,7 +509,7 @@ void* nwipe_gutmann( void* ptr )
|
||||
u16 s[27];
|
||||
|
||||
/* Load the array with random characters. */
|
||||
ssize_t r = read( c->entropy_fd, &s, sizeof( s ) );
|
||||
ssize_t r = nwipe_read_entropy( &s, sizeof( s ) );
|
||||
if( r != sizeof( s ) )
|
||||
{
|
||||
r = errno;
|
||||
@@ -611,7 +667,7 @@ void* nwipe_ops2( void* ptr )
|
||||
}
|
||||
|
||||
/* Load the array of random characters. */
|
||||
r = read( c->entropy_fd, s, u );
|
||||
r = nwipe_read_entropy( s, u );
|
||||
|
||||
if( r != u )
|
||||
{
|
||||
@@ -750,6 +806,86 @@ void* nwipe_random( void* ptr )
|
||||
return NULL;
|
||||
} /* nwipe_random */
|
||||
|
||||
void* nwipe_bruce7( void* ptr )
|
||||
{
|
||||
/**
|
||||
* Bruce Schneier 7-Pass wiping method.
|
||||
*
|
||||
* Pass 1: Overwrite the drive with all ones (0xFF).
|
||||
* Pass 2: Overwrite the drive with all zeroes (0x00).
|
||||
* Pass 3-7: Overwrite the drive with five passes of random data.
|
||||
*/
|
||||
|
||||
nwipe_context_t* c = (nwipe_context_t*) ptr;
|
||||
|
||||
/* Get current time at the start of the wipe */
|
||||
time( &c->start_time );
|
||||
|
||||
/* Set wipe in progress flag for GUI */
|
||||
c->wipe_status = 1;
|
||||
|
||||
/* Setup for Bruce Schneier 7-Pass method */
|
||||
char onefill[1] = { '\xFF' };
|
||||
char zerofill[1] = { '\x00' };
|
||||
nwipe_pattern_t patterns[] = {
|
||||
{ 1, &onefill[0] }, // Pass 1: Overwrite with ones
|
||||
{ 1, &zerofill[0] }, // Pass 2: Overwrite with zeroes
|
||||
{ -1, "" }, // Pass 3: Random data
|
||||
{ -1, "" }, // Pass 4: Random data
|
||||
{ -1, "" }, // Pass 5: Random data
|
||||
{ -1, "" }, // Pass 6: Random data
|
||||
{ -1, "" }, // Pass 7: Random data
|
||||
{ 0, NULL } // Terminate pattern array
|
||||
};
|
||||
|
||||
/* Run the Bruce Schneier 7-Pass method */
|
||||
c->result = nwipe_runmethod( c, patterns );
|
||||
|
||||
/* Finished. Set the wipe_status flag so that the GUI knows */
|
||||
c->wipe_status = 0;
|
||||
|
||||
/* Get current time at the end of the wipe */
|
||||
time( &c->end_time );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* nwipe_bmb( void* ptr )
|
||||
{
|
||||
/**
|
||||
* BMB Secure Wipe Method:
|
||||
* Pass 1: 0xFF
|
||||
* Pass 2: 0x00
|
||||
* Pass 3-5: 3× Random
|
||||
* Pass 6: 0xFF
|
||||
*/
|
||||
|
||||
nwipe_context_t* c = (nwipe_context_t*) ptr;
|
||||
|
||||
time( &c->start_time );
|
||||
c->wipe_status = 1;
|
||||
|
||||
char onefill[1] = { '\xFF' };
|
||||
char zerofill[1] = { '\x00' };
|
||||
|
||||
nwipe_pattern_t patterns[] = {
|
||||
{ 1, &onefill[0] }, // 0xFF
|
||||
{ 1, &zerofill[0] }, // 0x00
|
||||
{ -1, "" }, // RANDOM
|
||||
{ -1, "" }, // RANDOM
|
||||
{ -1, "" }, // RANDOM
|
||||
{ 1, &onefill[0] }, // 0xFF
|
||||
{ 0, NULL } // 0X00
|
||||
};
|
||||
|
||||
c->result = nwipe_runmethod( c, patterns );
|
||||
|
||||
c->wipe_status = 0;
|
||||
time( &c->end_time );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns )
|
||||
{
|
||||
/**
|
||||
@@ -911,13 +1047,13 @@ int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns )
|
||||
c->pass_type = NWIPE_PASS_WRITE;
|
||||
|
||||
/* Seed the PRNG. */
|
||||
r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length );
|
||||
r = nwipe_read_entropy( c->prng_seed.s, c->prng_seed.length );
|
||||
|
||||
/* Check the result. */
|
||||
if( r < 0 )
|
||||
{
|
||||
c->pass_type = NWIPE_PASS_NONE;
|
||||
nwipe_perror( errno, __FUNCTION__, "read" );
|
||||
nwipe_perror( errno, __FUNCTION__, "getrandom" );
|
||||
nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." );
|
||||
return -1;
|
||||
}
|
||||
@@ -1014,12 +1150,12 @@ int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns )
|
||||
c->pass_type = NWIPE_PASS_FINAL_OPS2;
|
||||
|
||||
/* Seed the PRNG. */
|
||||
r = read( c->entropy_fd, c->prng_seed.s, c->prng_seed.length );
|
||||
r = nwipe_read_entropy( c->prng_seed.s, c->prng_seed.length );
|
||||
|
||||
/* Check the result. */
|
||||
if( r < 0 )
|
||||
{
|
||||
nwipe_perror( errno, __FUNCTION__, "read" );
|
||||
nwipe_perror( errno, __FUNCTION__, "getrandom" );
|
||||
nwipe_log( NWIPE_LOG_FATAL, "Unable to seed the PRNG." );
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ void* nwipe_zero( void* ptr );
|
||||
void* nwipe_one( void* ptr );
|
||||
void* nwipe_verify_zero( void* ptr );
|
||||
void* nwipe_verify_one( void* ptr );
|
||||
void* nwipe_bruce7( void* ptr );
|
||||
void* nwipe_bmb( void* ptr );
|
||||
|
||||
void calculate_round_size( nwipe_context_t* );
|
||||
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
#include "context.h"
|
||||
#include "logging.h"
|
||||
#include "miscellaneous.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h> // for isspace()
|
||||
#include <stddef.h> // for NULL
|
||||
|
||||
/* Convert string to upper case
|
||||
*/
|
||||
@@ -684,6 +692,22 @@ void fix_endian_model_names( char* model )
|
||||
{
|
||||
swap_endian_flag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Sundisk - Sandisk SSD from Sun AXI */
|
||||
if( !( strncmp( model_lower_case, "usdnsi k", 8 ) ) )
|
||||
{
|
||||
swap_endian_flag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Sandisk */
|
||||
if( !( strncmp( model_lower_case, "asdnsi k", 8 ) ) )
|
||||
{
|
||||
swap_endian_flag = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -723,3 +747,150 @@ void fix_endian_model_names( char* model )
|
||||
free( tmp_string );
|
||||
free( model_lower_case );
|
||||
}
|
||||
|
||||
/*
|
||||
* get_device_uuid - Find the UUID corresponding to a given block device
|
||||
* @device_path: e.g., "/dev/sda1"
|
||||
* @uuid_buf: pointer to a 40-byte buffer where the UUID string will be stored
|
||||
*
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int get_device_uuid( const char* device_path, char* uuid_buf )
|
||||
{
|
||||
const char* uuid_dir = "/dev/disk/by-uuid";
|
||||
struct dirent* entry;
|
||||
DIR* dir;
|
||||
char link_path[PATH_MAX];
|
||||
char resolved_path[PATH_MAX];
|
||||
ssize_t len;
|
||||
|
||||
if( !device_path || !uuid_buf )
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dir = opendir( uuid_dir );
|
||||
if( !dir )
|
||||
{
|
||||
perror( "opendir" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
while( ( entry = readdir( dir ) ) != NULL )
|
||||
{
|
||||
// Skip "." and ".."
|
||||
if( strcmp( entry->d_name, "." ) == 0 || strcmp( entry->d_name, ".." ) == 0 )
|
||||
continue;
|
||||
|
||||
snprintf( link_path, sizeof( link_path ), "%s/%s", uuid_dir, entry->d_name );
|
||||
|
||||
// readlink() doesn't null-terminate, so we must handle that
|
||||
len = readlink( link_path, resolved_path, sizeof( resolved_path ) - 1 );
|
||||
if( len == -1 )
|
||||
continue; // skip unreadable links
|
||||
|
||||
resolved_path[len] = '\0';
|
||||
|
||||
// Construct full path (readlink gives a relative path, usually "../../sda1")
|
||||
char full_path[PATH_MAX + strlen( uuid_dir ) + 1];
|
||||
if( resolved_path[0] != '/' )
|
||||
{
|
||||
// Resolve relative symlink path
|
||||
snprintf( full_path, sizeof( full_path ), "%s/%s", uuid_dir, resolved_path );
|
||||
if( realpath( full_path, full_path ) == NULL )
|
||||
{
|
||||
closedir( dir );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy( full_path, resolved_path, sizeof( full_path ) );
|
||||
full_path[sizeof( full_path ) - 1] = '\0';
|
||||
}
|
||||
|
||||
// Compare with the given device path
|
||||
if( strcmp( full_path, device_path ) == 0 )
|
||||
{
|
||||
strncpy( uuid_buf, entry->d_name, 40 );
|
||||
uuid_buf[39] = '\0';
|
||||
closedir( dir );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
closedir( dir );
|
||||
errno = ENOENT;
|
||||
return -1; // not found
|
||||
}
|
||||
|
||||
/**
|
||||
* find_base_device - searches /sys/block for a device that matches
|
||||
* the prefix of the provided name.
|
||||
*
|
||||
* @devname: the input device name (e.g. "sda1", "sdb2")
|
||||
* @result: buffer to store the matched base device name
|
||||
* @size: size of the result buffer
|
||||
*
|
||||
* Returns: 0 on success (match found), -1 on failure (no match or error)
|
||||
*/
|
||||
int find_base_device( const char* devname, char* result, size_t size )
|
||||
{
|
||||
if( !devname || !result || size == 0 )
|
||||
{
|
||||
fprintf( stderr, "Invalid argument(s)\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
DIR* dir = opendir( "/sys/block" );
|
||||
if( !dir )
|
||||
{
|
||||
perror( "opendir" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dirent* entry;
|
||||
int found = -1;
|
||||
|
||||
while( ( entry = readdir( dir ) ) != NULL )
|
||||
{
|
||||
// Skip "." and ".."
|
||||
if( entry->d_name[0] == '.' )
|
||||
continue;
|
||||
|
||||
// Check if the given devname starts with the block device name
|
||||
size_t len = strlen( entry->d_name );
|
||||
if( strncmp( devname, entry->d_name, len ) == 0 )
|
||||
{
|
||||
// Match found
|
||||
strncpy( result, entry->d_name, size - 1 );
|
||||
result[size - 1] = '\0';
|
||||
found = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir( dir );
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* skip_whitespace - Returns a pointer to the first non-whitespace character
|
||||
* in a given string.
|
||||
*
|
||||
* @str: input string
|
||||
*
|
||||
* Returns: pointer to the first non-whitespace character,
|
||||
* or NULL if str is NULL or the string contains only whitespace.
|
||||
*/
|
||||
const char* skip_whitespace( const char* str )
|
||||
{
|
||||
if( !str )
|
||||
return NULL;
|
||||
|
||||
while( *str && isspace( (unsigned char) *str ) )
|
||||
str++;
|
||||
|
||||
return *str ? str : NULL;
|
||||
}
|
||||
|
||||
@@ -129,4 +129,36 @@ int write_system_datetime( char*, char*, char*, char*, char*, char* );
|
||||
*/
|
||||
void fix_endian_model_names( char* model );
|
||||
|
||||
/**
|
||||
* Obtains the UUID of the drive partition
|
||||
*
|
||||
* @param constchar* device path, example /dev/sda1
|
||||
* @param char* pointer to UUID string UUID_SIZE long
|
||||
* @return 0 = success, -1 = failure.
|
||||
*/
|
||||
int get_device_uuid( const char*, char* );
|
||||
|
||||
/**
|
||||
* find_base_device - searches /sys/block for a device that matches
|
||||
* the prefix of the provided name.
|
||||
*
|
||||
* @devname: the input device name (e.g. "sda1", "sdb2")
|
||||
* @result: buffer to store the matched base device name
|
||||
* @size: size of the result buffer
|
||||
*
|
||||
* Returns: 0 on success (match found), -1 on failure (no match or error)
|
||||
*/
|
||||
int find_base_device( const char*, char*, size_t );
|
||||
|
||||
/**
|
||||
* skip_whitespace - Returns a pointer to the first non-whitespace character
|
||||
* in a given string.
|
||||
*
|
||||
* @str: input string
|
||||
*
|
||||
* Returns: pointer to the first non-whitespace character,
|
||||
* or NULL if str is NULL or the string contains only whitespace.
|
||||
*/
|
||||
const char* skip_whitespace( const char* );
|
||||
|
||||
#endif /* HPA_DCO_H_ */
|
||||
|
||||
505
src/nwipe.c
505
src/nwipe.c
@@ -27,6 +27,11 @@
|
||||
#define _POSIX_SOURCE
|
||||
#endif
|
||||
|
||||
/* Enable GNU extensions so that O_DIRECT is visible from <fcntl.h>. */
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE 1
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -59,6 +64,18 @@
|
||||
#include "hpa_dco.h"
|
||||
#include "conf.h"
|
||||
#include <libconfig.h>
|
||||
#include <fcntl.h> /* O_DIRECT, O_RDWR, ... */
|
||||
|
||||
#ifdef NWIPE_USE_DIRECT_IO
|
||||
#ifndef O_DIRECT
|
||||
/*
|
||||
* Some platforms or libcs do not define O_DIRECT at all. Defining it
|
||||
* as 0 makes the flag a no-op and keeps the code buildable.
|
||||
* On Linux/glibc, <fcntl.h> via nwipe.h will provide a real O_DIRECT.
|
||||
*/
|
||||
#define O_DIRECT 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int terminate_signal;
|
||||
int user_abort;
|
||||
@@ -79,6 +96,189 @@ int devnamecmp( const void* a, const void* b )
|
||||
return ( ret );
|
||||
}
|
||||
|
||||
static int nwipe_prng_bench_cmp_desc( const void* a, const void* b )
|
||||
{
|
||||
const nwipe_prng_bench_result_t* A = (const nwipe_prng_bench_result_t*) a;
|
||||
const nwipe_prng_bench_result_t* B = (const nwipe_prng_bench_result_t*) b;
|
||||
|
||||
/* successful results first */
|
||||
if( A->rc != 0 && B->rc == 0 )
|
||||
return 1;
|
||||
if( A->rc == 0 && B->rc != 0 )
|
||||
return -1;
|
||||
|
||||
/* then sort by MB/s */
|
||||
if( A->mbps < B->mbps )
|
||||
return 1;
|
||||
if( A->mbps > B->mbps )
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NWIPE_PDF_DIR_MODE ( S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH )
|
||||
/* -> 0755: rwx for owner, r-x for group and others */
|
||||
|
||||
/* Helper: try to create and remove a temporary file inside the directory.
|
||||
* This catches cases where access(path, W_OK) passes (especially as root)
|
||||
* but the underlying filesystem does not allow creating regular files,
|
||||
* e.g. /proc or other pseudo/readonly filesystems.
|
||||
*/
|
||||
static int nwipe_probe_directory_writable( const char* path )
|
||||
{
|
||||
const char* suffix = "/.nwipe_pdf_testXXXXXX";
|
||||
size_t path_len = strlen( path );
|
||||
size_t suffix_len = strlen( suffix );
|
||||
size_t total_len = path_len + suffix_len + 1; /* +1 for '\0' */
|
||||
|
||||
char* tmpl = (char*) malloc( total_len );
|
||||
if( tmpl == NULL )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "Failed to allocate memory to probe PDFreportpath '%s'.", path );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Build template "<path>/.nwipe_pdf_testXXXXXX" */
|
||||
snprintf( tmpl, total_len, "%s%s", path, suffix );
|
||||
|
||||
int fd = mkstemp( tmpl );
|
||||
if( fd < 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR,
|
||||
"PDFreportpath '%s' is not writable (cannot create test file): %s.",
|
||||
path,
|
||||
strerror( errno ) );
|
||||
free( tmpl );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Successfully created a temporary file, now clean it up. */
|
||||
close( fd );
|
||||
if( unlink( tmpl ) != 0 )
|
||||
{
|
||||
/* Not fatal for our check, but log it anyway. */
|
||||
nwipe_log( NWIPE_LOG_WARNING,
|
||||
"Failed to remove temporary test file '%s' in PDFreportpath '%s': %s.",
|
||||
tmpl,
|
||||
path,
|
||||
strerror( errno ) );
|
||||
}
|
||||
|
||||
free( tmpl );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nwipe_ensure_directory( const char* path )
|
||||
{
|
||||
struct stat st;
|
||||
char* tmp;
|
||||
char* p;
|
||||
size_t len;
|
||||
|
||||
if( path == NULL || path[0] == '\0' )
|
||||
{
|
||||
/* Empty path: nothing to do, treat as success. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 1. First try: does the path already exist? */
|
||||
if( stat( path, &st ) == 0 )
|
||||
{
|
||||
/* Path exists; make sure it's a directory. */
|
||||
if( !S_ISDIR( st.st_mode ) )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath '%s' exists but is not a directory.", path );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Even if access() says it's writable (especially as root),
|
||||
* we still probe by actually creating a test file. */
|
||||
if( nwipe_probe_directory_writable( path ) != 0 )
|
||||
{
|
||||
/* Detailed error already logged. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Everything is fine, directory already present and writable. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stat() failed: if this is not "does not exist", propagate the error. */
|
||||
if( errno != ENOENT )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "Failed to stat PDFreportpath '%s': %s.", path, strerror( errno ) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 2. Directory does not exist -> create it recursively (mkdir -p style). */
|
||||
|
||||
len = strlen( path );
|
||||
tmp = (char*) malloc( len + 1 );
|
||||
if( tmp == NULL )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "Failed to allocate memory to create PDFreportpath '%s'.", path );
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy( tmp, path, len + 1 );
|
||||
|
||||
/* Start at the beginning of the string.
|
||||
* For absolute paths ("/foo/bar") we skip the leading slash so we do not
|
||||
* try to create "/" itself. */
|
||||
p = tmp;
|
||||
if( tmp[0] == '/' )
|
||||
{
|
||||
p = tmp + 1;
|
||||
}
|
||||
|
||||
for( ; *p; ++p )
|
||||
{
|
||||
if( *p == '/' )
|
||||
{
|
||||
*p = '\0';
|
||||
|
||||
/* Skip empty components (can happen with leading '/' or double '//'). */
|
||||
if( tmp[0] != '\0' )
|
||||
{
|
||||
if( mkdir( tmp, NWIPE_PDF_DIR_MODE ) != 0 && errno != EEXIST )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR,
|
||||
"Failed to create directory '%s' for PDFreportpath '%s': %s.",
|
||||
tmp,
|
||||
path,
|
||||
strerror( errno ) );
|
||||
free( tmp );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the final directory component (the full path). */
|
||||
if( mkdir( tmp, NWIPE_PDF_DIR_MODE ) != 0 && errno != EEXIST )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR,
|
||||
"Failed to create directory '%s' for PDFreportpath '%s': %s.",
|
||||
tmp,
|
||||
path,
|
||||
strerror( errno ) );
|
||||
free( tmp );
|
||||
return -1;
|
||||
}
|
||||
|
||||
free( tmp );
|
||||
|
||||
/* 3. Final sanity check: ensure the path is writable by probing with a file. */
|
||||
if( nwipe_probe_directory_writable( path ) != 0 )
|
||||
{
|
||||
/* Detailed error already logged. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
int nwipe_optind; // The result of nwipe_options().
|
||||
@@ -97,9 +297,6 @@ int main( int argc, char** argv )
|
||||
char module_shortform[50];
|
||||
char final_cmd_modprobe[sizeof( modprobe_command ) + sizeof( module_shortform )];
|
||||
|
||||
/* The entropy source file handle. */
|
||||
int nwipe_entropy;
|
||||
|
||||
/* The generic index variables. */
|
||||
int i;
|
||||
int j;
|
||||
@@ -161,6 +358,133 @@ int main( int argc, char** argv )
|
||||
/* Log OS info */
|
||||
nwipe_log_OSinfo();
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* PRNG benchmark / auto-select (runs before device scan)
|
||||
* ------------------------------------------------------------ */
|
||||
if( nwipe_options.prng_benchmark_only || nwipe_options.prng_auto )
|
||||
{
|
||||
/* tune defaults */
|
||||
const size_t io_block = 4 * 1024 * 1024; /* 4 MiB RAM buffer blocks */
|
||||
double seconds = nwipe_options.prng_bench_seconds;
|
||||
|
||||
/* If user requested auto-select and didn't override seconds, keep it short */
|
||||
if( nwipe_options.prng_auto )
|
||||
{
|
||||
if( seconds <= 0.0 )
|
||||
seconds = 0.25;
|
||||
/* Optional: if you consider "1.0" to be the default and want auto shorter:
|
||||
* if( seconds == 1.0 ) seconds = 0.25;
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if( seconds <= 0.0 )
|
||||
seconds = 1.0;
|
||||
}
|
||||
|
||||
nwipe_prng_bench_result_t results[16];
|
||||
memset( results, 0, sizeof( results ) );
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* --prng-benchmark-only path
|
||||
* (keep output clean: no live "Testing..." lines by default)
|
||||
* ------------------------------------------------------------ */
|
||||
if( nwipe_options.prng_benchmark_only )
|
||||
{
|
||||
const int live_print = 0; /* set to 1 if you also want live here */
|
||||
|
||||
int n = nwipe_prng_benchmark_all_live(
|
||||
seconds, io_block, results, (int) ( sizeof( results ) / sizeof( results[0] ) ), live_print );
|
||||
|
||||
if( n <= 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PRNG benchmark failed (no results)." );
|
||||
printf( "PRNG benchmark failed (no results).\n" );
|
||||
cleanup();
|
||||
exit( 3 );
|
||||
}
|
||||
|
||||
qsort( results, (size_t) n, sizeof( results[0] ), nwipe_prng_bench_cmp_desc );
|
||||
|
||||
/* Print to console + log */
|
||||
printf( "\nPRNG Benchmark (RAM-only) ~%.2fs each, block=%zu MiB\n", seconds, io_block / ( 1024 * 1024 ) );
|
||||
printf( "---------------------------------------------------\n" );
|
||||
|
||||
nwipe_log( NWIPE_LOG_INFO,
|
||||
"PRNG Benchmark (RAM-only) ~%.2fs each, block=%zu MiB",
|
||||
seconds,
|
||||
io_block / ( 1024 * 1024 ) );
|
||||
|
||||
for( int i = 0; i < n; i++ )
|
||||
{
|
||||
if( results[i].rc == 0 )
|
||||
{
|
||||
printf( "%2d) %-40s %10.1f MB/s\n", i + 1, results[i].prng->label, results[i].mbps );
|
||||
nwipe_log( NWIPE_LOG_INFO,
|
||||
"PRNG bench %2d) %-40s %10.1f MB/s",
|
||||
i + 1,
|
||||
results[i].prng->label,
|
||||
results[i].mbps );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "%2d) %-40s (failed: rc=%d)\n", i + 1, results[i].prng->label, results[i].rc );
|
||||
nwipe_log( NWIPE_LOG_WARNING,
|
||||
"PRNG bench %2d) %-40s (failed: rc=%d)",
|
||||
i + 1,
|
||||
results[i].prng->label,
|
||||
results[i].rc );
|
||||
}
|
||||
}
|
||||
|
||||
printf( "\n" );
|
||||
cleanup();
|
||||
exit( 0 );
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
* --prng=auto path
|
||||
* (THIS is the path where live output matters for “GUI delay”)
|
||||
* ------------------------------------------------------------ */
|
||||
if( nwipe_options.prng_auto )
|
||||
{
|
||||
/* live_print=1: prints:
|
||||
* - "Analysing PRNG performance:" immediately (with spinner)
|
||||
* - "Testing <PRNG> performance..." per PRNG
|
||||
* - "<PRNG> -> xx.x MB/s" immediately after each PRNG
|
||||
*/
|
||||
const int live_print = 1;
|
||||
|
||||
/* Option A (preferred): make select_fastest call the live benchmark internally.
|
||||
* best = nwipe_prng_select_fastest(seconds, io_block, results, count);
|
||||
* and inside select_fastest use nwipe_prng_benchmark_all_live(..., 1)
|
||||
*
|
||||
* Option B: benchmark here (live), then choose best locally.
|
||||
* Since you already have nwipe_prng_select_fastest(), stick with Option A.
|
||||
*/
|
||||
|
||||
const nwipe_prng_t* best = nwipe_prng_select_fastest(
|
||||
seconds, io_block, results, (int) ( sizeof( results ) / sizeof( results[0] ) ) /* results_count */
|
||||
/* ensure select_fastest uses nwipe_prng_benchmark_all_live(..., live_print) */
|
||||
);
|
||||
|
||||
if( best != NULL )
|
||||
{
|
||||
/* Apply selection */
|
||||
nwipe_options.prng = (nwipe_prng_t*) best;
|
||||
|
||||
nwipe_log( NWIPE_LOG_INFO, "Auto-selected fastest PRNG: %s", best->label );
|
||||
printf( "Auto-selected fastest PRNG: %s\n", best->label );
|
||||
}
|
||||
else
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_WARNING,
|
||||
"Auto PRNG selection: no working PRNG found, keeping configured default." );
|
||||
printf( "Auto PRNG selection: no working PRNG found, keeping configured default.\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that hdparm exists, we use hdparm for some HPA/DCO detection etc, if not
|
||||
* exit nwipe. These checks are required if the PATH environment is not setup !
|
||||
* Example: Debian sid 'su' as opposed to 'su -'
|
||||
@@ -184,12 +508,14 @@ int main( int argc, char** argv )
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if the given path for PDF reports is a writeable directory */
|
||||
/* Check if the given path for PDF reports is a writeable directory.
|
||||
* If it does not exist, try to create it (mkdir -p style).
|
||||
*/
|
||||
if( strcmp( nwipe_options.PDFreportpath, "noPDF" ) != 0 )
|
||||
{
|
||||
if( access( nwipe_options.PDFreportpath, W_OK ) != 0 )
|
||||
if( nwipe_ensure_directory( nwipe_options.PDFreportpath ) != 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PDFreportpath %s is not a writeable directory.", nwipe_options.PDFreportpath );
|
||||
/* nwipe_ensure_directory already logged a detailed error message. */
|
||||
cleanup();
|
||||
exit( 2 );
|
||||
}
|
||||
@@ -256,21 +582,6 @@ int main( int argc, char** argv )
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
/* Open the entropy source. */
|
||||
nwipe_entropy = open( NWIPE_KNOB_ENTROPY, O_RDONLY );
|
||||
|
||||
/* Check the result. */
|
||||
if( nwipe_entropy < 0 )
|
||||
{
|
||||
nwipe_perror( errno, __FUNCTION__, "open" );
|
||||
nwipe_log( NWIPE_LOG_FATAL, "Unable to open entropy source %s.", NWIPE_KNOB_ENTROPY );
|
||||
cleanup();
|
||||
free( c2 );
|
||||
return errno;
|
||||
}
|
||||
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Opened entropy source '%s'.", NWIPE_KNOB_ENTROPY );
|
||||
|
||||
/* Block relevant signals in main thread. Any other threads that are */
|
||||
/* created after this will also block those signals. */
|
||||
sigset_t sigset;
|
||||
@@ -359,10 +670,7 @@ int main( int argc, char** argv )
|
||||
for( i = 0; i < nwipe_enumerated; i++ )
|
||||
{
|
||||
|
||||
/* Set the entropy source. */
|
||||
c1[i]->entropy_fd = nwipe_entropy;
|
||||
|
||||
if( nwipe_options.autonuke )
|
||||
if( nwipe_options.autonuke == 1 )
|
||||
{
|
||||
/* When the autonuke option is set, select all disks. */
|
||||
// TODO - partitions
|
||||
@@ -410,33 +718,54 @@ int main( int argc, char** argv )
|
||||
&nwipe_temperature_thread, NULL, nwipe_update_temperature_thread, &nwipe_temperature_thread_data );
|
||||
|
||||
/* Start the ncurses interface. */
|
||||
if( !nwipe_options.nogui )
|
||||
nwipe_gui_init();
|
||||
|
||||
if( nwipe_options.autonuke == 1 )
|
||||
switch( nwipe_options.nogui )
|
||||
{
|
||||
/* Print the options window. */
|
||||
if( !nwipe_options.nogui )
|
||||
nwipe_gui_options();
|
||||
case 0:
|
||||
nwipe_gui_init();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
break;
|
||||
|
||||
default:
|
||||
printf( "system error: nwipe_options.nogui (should be 0 or 1) is invalid, nwipe_options.nogui=%i !? \n",
|
||||
nwipe_options.nogui );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get device selections from the user. */
|
||||
if( nwipe_options.nogui )
|
||||
{
|
||||
printf( "--nogui option must be used with autonuke option\n" );
|
||||
cleanup();
|
||||
exit( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( nwipe_options.PDF_preview_details == 1 )
|
||||
{
|
||||
nwipe_gui_preview_org_customer( SHOWING_PRIOR_TO_DRIVE_SELECTION );
|
||||
}
|
||||
|
||||
nwipe_gui_select( nwipe_enumerated, c1 );
|
||||
}
|
||||
switch( nwipe_options.autonuke )
|
||||
{
|
||||
case 0:
|
||||
/* The user can't specify the nogui option without also using the autonuke option */
|
||||
if( nwipe_options.nogui == 1 )
|
||||
{
|
||||
printf( "--nogui option must be used with autonuke option\n" );
|
||||
cleanup();
|
||||
exit( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If selected show customer and organisation details BEFORE drive selection screen */
|
||||
if( nwipe_options.PDF_preview_details == 1 )
|
||||
{
|
||||
nwipe_gui_preview_org_customer( SHOWING_PRIOR_TO_DRIVE_SELECTION );
|
||||
}
|
||||
|
||||
/* Get device selections from the user. */
|
||||
nwipe_gui_select( nwipe_enumerated, c1 );
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* Print the options window. */
|
||||
if( !nwipe_options.nogui )
|
||||
nwipe_gui_options();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
printf(
|
||||
"system error: nwipe_options.autonuke (should be 0 or 1) is invalid, nwipe_options.autonuke=%i !? \n",
|
||||
nwipe_options.autonuke );
|
||||
}
|
||||
|
||||
/* Initialise some of the variables in the drive contexts
|
||||
@@ -499,8 +828,82 @@ int main( int argc, char** argv )
|
||||
/* Initialise the wipe_status flag, -1 = wipe not yet started */
|
||||
c2[i]->wipe_status = -1;
|
||||
|
||||
/* Open the file for reads and writes. */
|
||||
c2[i]->device_fd = open( c2[i]->device_name, O_RDWR );
|
||||
/* Open the file for reads and writes, honoring the configured I/O mode. */
|
||||
int open_flags = O_RDWR;
|
||||
|
||||
#ifdef NWIPE_USE_DIRECT_IO
|
||||
/*
|
||||
* Decide whether to request O_DIRECT based on the runtime I/O mode:
|
||||
* auto -> try O_DIRECT, fall back to cached I/O if needed
|
||||
* direct -> force O_DIRECT, fail hard if not supported
|
||||
* cached -> do not request O_DIRECT at all
|
||||
*/
|
||||
if( nwipe_options.io_mode == NWIPE_IO_MODE_DIRECT || nwipe_options.io_mode == NWIPE_IO_MODE_AUTO )
|
||||
{
|
||||
open_flags |= O_DIRECT;
|
||||
}
|
||||
#endif
|
||||
|
||||
c2[i]->device_fd = open( c2[i]->device_name, open_flags );
|
||||
|
||||
#ifdef NWIPE_USE_DIRECT_IO
|
||||
if( c2[i]->device_fd < 0 && ( errno == EINVAL || errno == EOPNOTSUPP ) )
|
||||
{
|
||||
if( nwipe_options.io_mode == NWIPE_IO_MODE_DIRECT )
|
||||
{
|
||||
/*
|
||||
* User explicitly requested direct I/O: do not silently
|
||||
* fall back. Mark the device as unusable and continue.
|
||||
*/
|
||||
nwipe_perror( errno, __FUNCTION__, "open" );
|
||||
nwipe_log( NWIPE_LOG_FATAL,
|
||||
"O_DIRECT requested via --directio but not supported on '%s'.",
|
||||
c2[i]->device_name );
|
||||
c2[i]->select = NWIPE_SELECT_DISABLED;
|
||||
continue;
|
||||
}
|
||||
else if( nwipe_options.io_mode == NWIPE_IO_MODE_AUTO )
|
||||
{
|
||||
/*
|
||||
* Auto mode: transparently fall back to cached I/O and
|
||||
* log a warning.
|
||||
*/
|
||||
nwipe_log( NWIPE_LOG_WARNING,
|
||||
"O_DIRECT not supported on '%s', falling back to cached I/O.",
|
||||
c2[i]->device_name );
|
||||
|
||||
open_flags &= ~O_DIRECT;
|
||||
c2[i]->device_fd = open( c2[i]->device_name, open_flags );
|
||||
}
|
||||
}
|
||||
|
||||
if( c2[i]->device_fd >= 0 )
|
||||
{
|
||||
const char* io_desc;
|
||||
|
||||
if( open_flags & O_DIRECT )
|
||||
{
|
||||
io_desc = "direct I/O (O_DIRECT)";
|
||||
c2[i]->io_mode = NWIPE_IO_MODE_DIRECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
io_desc = "cached I/O";
|
||||
c2[i]->io_mode = NWIPE_IO_MODE_CACHED;
|
||||
}
|
||||
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Using %s on device '%s'.", io_desc, c2[i]->device_name );
|
||||
}
|
||||
#endif /* NWIPE_USE_DIRECT_IO */
|
||||
|
||||
/* Check the open() result (after any fallback logic). */
|
||||
if( c2[i]->device_fd < 0 )
|
||||
{
|
||||
nwipe_perror( errno, __FUNCTION__, "open" );
|
||||
nwipe_log( NWIPE_LOG_WARNING, "Unable to open device '%s'.", c2[i]->device_name );
|
||||
c2[i]->select = NWIPE_SELECT_DISABLED;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check the open() result. */
|
||||
if( c2[i]->device_fd < 0 )
|
||||
|
||||
542
src/options.c
542
src/options.c
@@ -28,6 +28,8 @@
|
||||
#include "logging.h"
|
||||
#include "version.h"
|
||||
#include "conf.h"
|
||||
#include "cpu_features.h"
|
||||
#include "libconfig.h"
|
||||
|
||||
/* The global options struct. */
|
||||
nwipe_options_t nwipe_options;
|
||||
@@ -44,6 +46,11 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
extern nwipe_prng_t nwipe_isaac64;
|
||||
extern nwipe_prng_t nwipe_add_lagg_fibonacci_prng;
|
||||
extern nwipe_prng_t nwipe_xoroshiro256_prng;
|
||||
extern nwipe_prng_t nwipe_aes_ctr_prng;
|
||||
|
||||
extern config_t nwipe_cfg;
|
||||
config_setting_t* setting;
|
||||
const char* user_defined_tag;
|
||||
|
||||
/* The getopt() result holder. */
|
||||
int nwipe_opt;
|
||||
@@ -73,7 +80,7 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
{ "autopoweroff", no_argument, 0, 0 },
|
||||
|
||||
/* A GNU standard option. Corresponds to the 'h' short option. */
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "help", no_argument, 0, 0 },
|
||||
|
||||
/* The wipe method. Corresponds to the 'm' short option. */
|
||||
{ "method", required_argument, 0, 'm' },
|
||||
@@ -89,6 +96,8 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
|
||||
/* The Pseudo Random Number Generator. */
|
||||
{ "prng", required_argument, 0, 'p' },
|
||||
{ "prng-benchmark", no_argument, 0, 0 },
|
||||
{ "prng-bench-seconds", required_argument, 0, 0 },
|
||||
|
||||
/* The number of times to run the method. */
|
||||
{ "rounds", required_argument, 0, 'r' },
|
||||
@@ -109,7 +118,7 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
{ "nogui", no_argument, 0, 0 },
|
||||
|
||||
/* Whether to anonymize the serial numbers. */
|
||||
{ "quiet", no_argument, 0, 'q' },
|
||||
{ "quiet", no_argument, 0, 0 },
|
||||
|
||||
/* A flag to indicate whether the devices would be opened in sync mode. */
|
||||
{ "sync", required_argument, 0, 0 },
|
||||
@@ -117,11 +126,18 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
/* Verify that wipe patterns are being written to the device. */
|
||||
{ "verify", required_argument, 0, 0 },
|
||||
|
||||
/* Display program version. */
|
||||
{ "verbose", no_argument, 0, 'v' },
|
||||
/* I/O mode selection: auto/direct/cached. */
|
||||
{ "directio", no_argument, 0, 0 },
|
||||
{ "cachedio", no_argument, 0, 0 },
|
||||
|
||||
/* Enables a field on the PDF that holds a tag that identifies the host computer */
|
||||
{ "pdftag", no_argument, 0, 0 },
|
||||
|
||||
/* Display program version. */
|
||||
{ "version", no_argument, 0, 'V' },
|
||||
{ "verbose", no_argument, 0, 0 },
|
||||
|
||||
/* Display program version. */
|
||||
{ "version", no_argument, 0, 0 },
|
||||
|
||||
/* Requisite padding for getopt(). */
|
||||
{ 0, 0, 0, 0 } };
|
||||
@@ -130,8 +146,30 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
nwipe_options.autonuke = 0;
|
||||
nwipe_options.autopoweroff = 0;
|
||||
nwipe_options.method = &nwipe_random;
|
||||
nwipe_options.prng =
|
||||
( sizeof( unsigned long int ) >= 8 ) ? &nwipe_xoroshiro256_prng : &nwipe_add_lagg_fibonacci_prng;
|
||||
nwipe_options.prng_auto = 1; /* by default the PRNG is selected through the benchmark selection */
|
||||
nwipe_options.prng_benchmark_only = 0;
|
||||
nwipe_options.prng_bench_seconds = 1.0; /* default for interactive / manual */
|
||||
|
||||
/*
|
||||
* Determines and sets the default PRNG based on AES-NI support and system architecture.
|
||||
* It selects AES-CTR PRNG if AES-NI is supported, xoroshiro256 for 64-bit systems without AES-NI,
|
||||
* and add lagged Fibonacci for 32-bit systems.
|
||||
*/
|
||||
|
||||
if( has_aes_ni() )
|
||||
{
|
||||
nwipe_options.prng = &nwipe_aes_ctr_prng;
|
||||
}
|
||||
else if( sizeof( unsigned long int ) >= 8 )
|
||||
{
|
||||
nwipe_options.prng = &nwipe_xoroshiro256_prng;
|
||||
nwipe_log( NWIPE_LOG_WARNING, "CPU doesn't support AES New Instructions, opting for XORoshiro-256 instead." );
|
||||
}
|
||||
else
|
||||
{
|
||||
nwipe_options.prng = &nwipe_add_lagg_fibonacci_prng;
|
||||
}
|
||||
|
||||
nwipe_options.rounds = 1;
|
||||
nwipe_options.noblank = 0;
|
||||
nwipe_options.nousb = 0;
|
||||
@@ -142,11 +180,16 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
nwipe_options.sync = DEFAULT_SYNC_RATE;
|
||||
nwipe_options.verbose = 0;
|
||||
nwipe_options.verify = NWIPE_VERIFY_LAST;
|
||||
nwipe_options.io_mode = NWIPE_IO_MODE_AUTO; /* Default: auto-select I/O mode. */
|
||||
nwipe_options.PDF_toggle_host_info = 0; /* Default: host visibility on PDF disabled */
|
||||
nwipe_options.PDFtag = 0;
|
||||
memset( nwipe_options.logfile, '\0', sizeof( nwipe_options.logfile ) );
|
||||
memset( nwipe_options.PDFreportpath, '\0', sizeof( nwipe_options.PDFreportpath ) );
|
||||
strncpy( nwipe_options.PDFreportpath, ".", 2 );
|
||||
|
||||
/* Read PDF settings from nwipe.conf if available */
|
||||
/*
|
||||
* Read PDF Enable/Disable settings from nwipe.conf if available
|
||||
*/
|
||||
if( ( ret = nwipe_conf_read_setting( "PDF_Certificate.PDF_Enable", &read_value ) ) )
|
||||
{
|
||||
/* error occurred */
|
||||
@@ -180,7 +223,63 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
}
|
||||
}
|
||||
|
||||
/* PDF Preview enable/disable */
|
||||
/*
|
||||
* Read PDF host visibility settings from nwipe.conf if available
|
||||
*/
|
||||
if( ( ret = nwipe_conf_read_setting( "PDF_Certificate.PDF_Host_Visibility", &read_value ) ) )
|
||||
{
|
||||
/* error occurred */
|
||||
nwipe_log(
|
||||
NWIPE_LOG_ERROR,
|
||||
"nwipe_conf_read_setting():Error reading PDF_Certificate.PDF_toggle_host_info from nwipe.conf, ret code %i",
|
||||
ret );
|
||||
|
||||
nwipe_options.PDF_toggle_host_info = 0; /* Disable host visibility on PDF */
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !strcmp( read_value, "ENABLED" ) )
|
||||
{
|
||||
nwipe_options.PDF_toggle_host_info = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !strcmp( read_value, "DISABLED" ) )
|
||||
{
|
||||
nwipe_options.PDF_toggle_host_info = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// error occurred
|
||||
nwipe_log( NWIPE_LOG_ERROR,
|
||||
"PDF_Certificate.PDF_toggle_host_info in nwipe.conf returned a value that was neither "
|
||||
"ENABLED or DISABLED" );
|
||||
nwipe_options.PDF_toggle_host_info = 0; // Default to disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read PDF tag Enable/Disable settings from nwipe.conf if available
|
||||
*/
|
||||
|
||||
setting = config_lookup( &nwipe_cfg, "PDF_Certificate" );
|
||||
|
||||
if( config_setting_lookup_string( setting, "User_Defined_Tag", &user_defined_tag ) )
|
||||
{
|
||||
if( user_defined_tag[0] != 0 )
|
||||
{
|
||||
nwipe_options.PDFtag = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
nwipe_options.PDFtag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PDF Preview enable/disable
|
||||
*/
|
||||
if( ( ret = nwipe_conf_read_setting( "PDF_Certificate.PDF_Preview", &read_value ) ) )
|
||||
{
|
||||
/* error occurred */
|
||||
@@ -214,7 +313,9 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialise each of the strings in the excluded drives array */
|
||||
/*
|
||||
* Initialise each of the strings in the excluded drives array
|
||||
*/
|
||||
for( i = 0; i < MAX_NUMBER_EXCLUDED_DRIVES; i++ )
|
||||
{
|
||||
nwipe_options.exclude[i][0] = 0;
|
||||
@@ -238,51 +339,193 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "autonuke" ) == 0 )
|
||||
{
|
||||
nwipe_options.autonuke = 1;
|
||||
break;
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--autonuke" ) == 0 )
|
||||
{
|
||||
nwipe_options.autonuke = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --autonuke?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "autopoweroff" ) == 0 )
|
||||
{
|
||||
nwipe_options.autopoweroff = 1;
|
||||
break;
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--autopoweroff" ) == 0 )
|
||||
{
|
||||
nwipe_options.autopoweroff = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --autopoweroff?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "help" ) == 0 )
|
||||
{
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--help" ) == 0 )
|
||||
{
|
||||
display_help();
|
||||
exit( EINVAL );
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --help?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "noblank" ) == 0 )
|
||||
{
|
||||
nwipe_options.noblank = 1;
|
||||
break;
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--noblank" ) == 0 )
|
||||
{
|
||||
nwipe_options.noblank = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --noblank?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "nousb" ) == 0 )
|
||||
{
|
||||
nwipe_options.nousb = 1;
|
||||
break;
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--nousb" ) == 0 )
|
||||
{
|
||||
nwipe_options.nousb = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --nousb?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "nowait" ) == 0 )
|
||||
{
|
||||
nwipe_options.nowait = 1;
|
||||
break;
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--nowait" ) == 0 )
|
||||
{
|
||||
nwipe_options.nowait = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --nowait?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "nosignals" ) == 0 )
|
||||
{
|
||||
nwipe_options.nosignals = 1;
|
||||
break;
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--nosignals" ) == 0 )
|
||||
{
|
||||
nwipe_options.nosignals = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --nosignals?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "nogui" ) == 0 )
|
||||
{
|
||||
nwipe_options.nogui = 1;
|
||||
nwipe_options.nowait = 1;
|
||||
break;
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--nogui" ) == 0 )
|
||||
{
|
||||
nwipe_options.nogui = 1;
|
||||
nwipe_options.nowait = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --nogui?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "quiet" ) == 0 )
|
||||
{
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--quiet" ) == 0 )
|
||||
{
|
||||
nwipe_options.quiet = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --quiet?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "verbose" ) == 0 )
|
||||
{
|
||||
nwipe_options.verbose = 1;
|
||||
break;
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--verbose" ) == 0 )
|
||||
{
|
||||
nwipe_options.verbose = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --verbose?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "sync" ) == 0 )
|
||||
@@ -297,7 +540,6 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "verify" ) == 0 )
|
||||
{
|
||||
|
||||
if( strcmp( optarg, "0" ) == 0 || strcmp( optarg, "off" ) == 0 )
|
||||
{
|
||||
nwipe_options.verify = NWIPE_VERIFY_NONE;
|
||||
@@ -321,6 +563,67 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
exit( EINVAL );
|
||||
}
|
||||
|
||||
/* I/O mode selection options. */
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "directio" ) == 0 )
|
||||
{
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--directio" ) == 0 )
|
||||
{
|
||||
nwipe_options.io_mode = NWIPE_IO_MODE_DIRECT;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --directio?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "cachedio" ) == 0 )
|
||||
{
|
||||
/* check for the full option name, as getopt_long() allows abreviations and can lead to unintended
|
||||
* consequences when the user makes a typo */
|
||||
if( strcmp( argv[optind - 1], "--cachedio" ) == 0 )
|
||||
{
|
||||
nwipe_options.io_mode = NWIPE_IO_MODE_CACHED;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: Strict command line options required, did you mean --cachedio?, you typed "
|
||||
"%s.\nType `sudo nwipe --help` for options \n",
|
||||
argv[optind - 1] );
|
||||
exit( EINVAL );
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "pdftag" ) == 0 )
|
||||
{
|
||||
nwipe_options.PDFtag = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if( strcmp( nwipe_options_long[i].name, "prng-benchmark" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng_benchmark_only = 1;
|
||||
break;
|
||||
}
|
||||
if( strcmp( nwipe_options_long[i].name, "prng-bench-seconds" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng_bench_seconds = atof( optarg );
|
||||
if( nwipe_options.prng_bench_seconds < 0.05 )
|
||||
nwipe_options.prng_bench_seconds = 0.05;
|
||||
if( nwipe_options.prng_bench_seconds > 10.0 )
|
||||
nwipe_options.prng_bench_seconds = 10.0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* getopt_long should raise on invalid option, so we should never get here. */
|
||||
exit( EINVAL );
|
||||
|
||||
@@ -386,6 +689,16 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
nwipe_options.method = &nwipe_is5enh;
|
||||
break;
|
||||
}
|
||||
if( strcmp( optarg, "bruce7" ) == 0 )
|
||||
{
|
||||
nwipe_options.method = &nwipe_bruce7;
|
||||
break;
|
||||
}
|
||||
if( strcmp( optarg, "bmb" ) == 0 )
|
||||
{
|
||||
nwipe_options.method = &nwipe_bmb;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Else we do not know this wipe method. */
|
||||
fprintf( stderr, "Error: Unknown wipe method '%s'.\n", optarg );
|
||||
@@ -472,10 +785,30 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
case 'h': /* Display help. */
|
||||
|
||||
display_help();
|
||||
exit( EINVAL );
|
||||
break;
|
||||
|
||||
case 'p': /* PRNG option. */
|
||||
|
||||
/* Default behaviour is auto now, but allow explicit opt-out */
|
||||
if( strcmp( optarg, "auto" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng_auto = 1;
|
||||
/* keep current default as fallback until autoselect runs */
|
||||
break;
|
||||
}
|
||||
|
||||
/* NEW: disable auto and keep compiled-in default selection */
|
||||
if( strcmp( optarg, "default" ) == 0 || strcmp( optarg, "manual" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng_auto = 0;
|
||||
/* keep nwipe_options.prng as chosen by CPU heuristics above */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Any explicit PRNG selection implies auto off */
|
||||
nwipe_options.prng_auto = 0;
|
||||
|
||||
if( strcmp( optarg, "mersenne" ) == 0 || strcmp( optarg, "twister" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng = &nwipe_twister;
|
||||
@@ -493,18 +826,35 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
nwipe_options.prng = &nwipe_isaac64;
|
||||
break;
|
||||
}
|
||||
|
||||
if( strcmp( optarg, "add_lagg_fibonacci_prng" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng = &nwipe_add_lagg_fibonacci_prng;
|
||||
break;
|
||||
}
|
||||
|
||||
if( strcmp( optarg, "xoroshiro256_prng" ) == 0 )
|
||||
{
|
||||
nwipe_options.prng = &nwipe_xoroshiro256_prng;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Else we do not know this PRNG. */
|
||||
if( strcmp( optarg, "aes_ctr_prng" ) == 0 )
|
||||
{
|
||||
if( has_aes_ni() )
|
||||
{
|
||||
nwipe_options.prng = &nwipe_aes_ctr_prng;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr,
|
||||
"Error: aes_ctr_prng requires AES-NI on this build, "
|
||||
"but your CPU does not support AES-NI.\n" );
|
||||
exit( EINVAL );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf( stderr, "Error: Unknown prng '%s'.\n", optarg );
|
||||
exit( EINVAL );
|
||||
|
||||
@@ -536,13 +886,19 @@ int nwipe_options_parse( int argc, char** argv )
|
||||
default:
|
||||
|
||||
/* Bogus command line argument. */
|
||||
display_help();
|
||||
exit( EINVAL );
|
||||
|
||||
} /* method */
|
||||
|
||||
} /* command line options */
|
||||
|
||||
/* Disable blanking for ops2 and verify methods */
|
||||
if( nwipe_options.method == &nwipe_ops2 || nwipe_options.method == &nwipe_verify_zero
|
||||
|| nwipe_options.method == &nwipe_verify_one )
|
||||
{
|
||||
nwipe_options.noblank = 1;
|
||||
}
|
||||
|
||||
/* Return the number of options that were processed. */
|
||||
return optind;
|
||||
}
|
||||
@@ -554,6 +910,7 @@ void nwipe_options_log( void )
|
||||
extern nwipe_prng_t nwipe_isaac64;
|
||||
extern nwipe_prng_t nwipe_add_lagg_fibonacci_prng;
|
||||
extern nwipe_prng_t nwipe_xoroshiro256_prng;
|
||||
extern nwipe_prng_t nwipe_aes_ctr_prng;
|
||||
|
||||
/**
|
||||
* Prints a manifest of options to the log.
|
||||
@@ -561,7 +918,7 @@ void nwipe_options_log( void )
|
||||
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Program options are set as follows..." );
|
||||
|
||||
if( nwipe_options.autonuke )
|
||||
if( nwipe_options.autonuke == 1 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " autonuke = %i (on)", nwipe_options.autonuke );
|
||||
}
|
||||
@@ -605,37 +962,29 @@ void nwipe_options_log( void )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Mersenne Twister" );
|
||||
}
|
||||
else if( nwipe_options.prng == &nwipe_add_lagg_fibonacci_prng )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Lagged Fibonacci generator" );
|
||||
}
|
||||
else if( nwipe_options.prng == &nwipe_xoroshiro256_prng )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = XORoshiro-256" );
|
||||
}
|
||||
else if( nwipe_options.prng == &nwipe_aes_ctr_prng )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = AES-CTR New Instructions (EXPERIMENTAL!)" );
|
||||
}
|
||||
else if( nwipe_options.prng == &nwipe_isaac )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" );
|
||||
}
|
||||
else if( nwipe_options.prng == &nwipe_isaac64 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( nwipe_options.prng == &nwipe_add_lagg_fibonacci_prng )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Lagged Fibonacci generator (EXPERIMENTAL!)" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( nwipe_options.prng == &nwipe_xoroshiro256_prng )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = XORoshiro-256 (EXPERIMENTAL!)" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( nwipe_options.prng == &nwipe_isaac )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( nwipe_options.prng == &nwipe_isaac64 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Isaac64" );
|
||||
}
|
||||
else
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " prng = Undefined" );
|
||||
}
|
||||
|
||||
nwipe_log( NWIPE_LOG_NOTICE, " method = %s", nwipe_method_label( nwipe_options.method ) );
|
||||
@@ -697,6 +1046,9 @@ void display_help()
|
||||
puts( " last - Verify after the last pass" );
|
||||
puts( " all - Verify every pass" );
|
||||
puts( " " );
|
||||
puts( " --directio Force direct I/O (O_DIRECT); fail if not supported" );
|
||||
puts( " --cachedio Force kernel cached I/O; never attempt O_DIRECT" );
|
||||
puts( " --io-mode=MODE I/O mode: auto (default), direct, cached\n" );
|
||||
puts( " Please mind that HMG IS5 enhanced always verifies the" );
|
||||
puts( " last (PRNG) pass regardless of this option.\n" );
|
||||
puts( " -m, --method=METHOD The wiping method. See man page for more details." );
|
||||
@@ -710,31 +1062,55 @@ void display_help()
|
||||
puts( " one - Overwrite with ones (0xFF)" );
|
||||
puts( " verify_zero - Verifies disk is zero filled" );
|
||||
puts( " verify_one - Verifies disk is 0xFF filled" );
|
||||
puts( " is5enh - HMG IS5 enhanced\n" );
|
||||
puts( " is5enh - HMG IS5 enhanced\n" );
|
||||
puts( " bruce7 - Schneier Bruce 7-pass mixed pattern\n" );
|
||||
puts( " bmb - BMB21-2019 mixed pattern\n" );
|
||||
puts( " -l, --logfile=FILE Filename to log to. Default is STDOUT\n" );
|
||||
puts( " -P, --PDFreportpath=PATH Path to write PDF reports to. Default is \".\"" );
|
||||
puts( " If set to \"noPDF\" no PDF reports are written.\n" );
|
||||
puts( " -p, --prng=METHOD PRNG option (mersenne|twister|isaac|isaac64|add_lagg_fibonacci_prng)\n" );
|
||||
puts( " -q, --quiet Anonymize logs and the GUI by removing unique data, i.e." );
|
||||
puts( " serial numbers, LU WWN Device ID, and SMBIOS/DMI data" );
|
||||
puts( " XXXXXX = S/N exists, ????? = S/N not obtainable\n" );
|
||||
puts( " -r, --rounds=NUM Number of times to wipe the device using the selected" );
|
||||
puts( " method (default: 1)\n" );
|
||||
puts( " --noblank Do NOT blank disk after wipe" );
|
||||
puts( " (default is to complete a final blank pass)\n" );
|
||||
puts( " --nowait Do NOT wait for a key before exiting" );
|
||||
puts( " (default is to wait)\n" );
|
||||
puts( " --nosignals Do NOT allow signals to interrupt a wipe" );
|
||||
puts( " (default is to allow)\n" );
|
||||
puts( " --nogui Do NOT show the GUI interface. Automatically invokes" );
|
||||
puts( " the nowait option. Must be used with the --autonuke" );
|
||||
puts( " option. Send SIGUSR1 to log current stats\n" );
|
||||
puts( " --nousb Do NOT show or wipe any USB devices whether in GUI" );
|
||||
puts( " mode, --nogui or --autonuke modes.\n" );
|
||||
puts( " -e, --exclude=DEVICES Up to ten comma separated devices to be excluded" );
|
||||
puts( " --exclude=/dev/sdc" );
|
||||
puts( " --exclude=/dev/sdc,/dev/sdd" );
|
||||
puts( " --exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1\n" );
|
||||
puts( " -p, --prng=METHOD PRNG option "
|
||||
"(mersenne|twister|isaac|isaac64|add_lagg_fibonacci_prng|xoroshiro256_prng|aes_ctr_prng)\n" );
|
||||
puts( " --prng=auto (default)" );
|
||||
puts( " Automatically benchmark all available PRNGs at startup and" );
|
||||
puts( " select the fastest one for the current hardware." );
|
||||
puts( "" );
|
||||
|
||||
puts( " --prng=default" );
|
||||
puts( " Disable auto-selection and use the built-in default PRNG choice" );
|
||||
puts( " (CPU-based heuristic; no benchmarking)." );
|
||||
puts( " Alias: --prng=manual" );
|
||||
puts( "" );
|
||||
puts( " --prng-benchmark" );
|
||||
puts( " Run a RAM-only PRNG throughput benchmark and exit." );
|
||||
puts( " Prints a sorted leaderboard (MB/s). No wipe is performed." );
|
||||
puts( "" );
|
||||
puts( " --prng-bench-seconds=N" );
|
||||
puts( " Seconds per PRNG during benchmarking (default: 1.0)." );
|
||||
puts( " For --prng=auto this is automatically reduced unless set." );
|
||||
|
||||
puts( " -q, --quiet Anonymize logs and the GUI by removing unique data, i.e." );
|
||||
puts( " serial numbers, LU WWN Device ID, and SMBIOS/DMI data." );
|
||||
puts( " XXXXXX = S/N exists, ????? = S/N not obtainable\n" );
|
||||
puts( " -r, --rounds=NUM Number of times to wipe the device using the selected" );
|
||||
puts( " method. (default: 1)\n" );
|
||||
puts( " --noblank Do NOT blank disk after wipe." );
|
||||
puts( " (default is to complete a final blank pass)\n" );
|
||||
puts( " --nowait Do NOT wait for a key before exiting." );
|
||||
puts( " (default is to wait)\n" );
|
||||
puts( " --nosignals Do NOT allow signals to interrupt a wipe." );
|
||||
puts( " (default is to allow)\n" );
|
||||
puts( " --nogui Do NOT show the GUI interface. Automatically invokes" );
|
||||
puts( " the nowait option. Must be used with the --autonuke" );
|
||||
puts( " option. Send SIGUSR1 to log current stats.\n" );
|
||||
puts( " --nousb Do NOT show or wipe any USB devices whether in GUI" );
|
||||
puts( " mode, --nogui or --autonuke modes.\n" );
|
||||
puts( " --pdftag Enables a field on the PDF that holds a tag that\n" );
|
||||
puts( " identifies the host computer\n" );
|
||||
puts( " -e, --exclude=DEVICES Up to ten comma separated devices to be excluded." );
|
||||
puts( " --exclude=/dev/sdc" );
|
||||
puts( " --exclude=/dev/sdc,/dev/sdd" );
|
||||
puts( " --exclude=/dev/sdc,/dev/sdd,/dev/mapper/cryptswap1\n" );
|
||||
puts( " --exclude=/dev/disk/by-id/ata-XXXXXXXX" );
|
||||
puts( " --exclude=/dev/disk/by-path/pci-0000:00:17.0-ata-1\n" );
|
||||
puts( "" );
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
#ifndef OPTIONS_H_
|
||||
#define OPTIONS_H_
|
||||
|
||||
#include "method.h"
|
||||
|
||||
/* Program knobs. */
|
||||
#define NWIPE_KNOB_ENTROPY "/dev/urandom"
|
||||
#define NWIPE_KNOB_IDENTITY_SIZE 512
|
||||
#define NWIPE_KNOB_LABEL_SIZE 128
|
||||
#define NWIPE_KNOB_LOADAVG "/proc/loadavg"
|
||||
@@ -39,6 +40,7 @@
|
||||
#define MAX_DRIVE_PATH_LENGTH 200 // e.g. /dev/sda is only 8 characters long, so 200 should be plenty.
|
||||
#define DEFAULT_SYNC_RATE 100000
|
||||
#define PATHNAME_MAX 2048
|
||||
#define NWIPE_USE_DIRECT_IO
|
||||
|
||||
/* Function prototypes for loading options from the environment and command line. */
|
||||
int nwipe_options_parse( int argc, char** argv );
|
||||
@@ -56,6 +58,9 @@ typedef struct
|
||||
int nowait; // Do not wait for a final key before exiting.
|
||||
int nosignals; // Do not allow signals to interrupt a wipe.
|
||||
int nogui; // Do not show the GUI.
|
||||
int prng_auto; /* 1 = auto-select fastest PRNG at startup */
|
||||
int prng_benchmark_only; /* 1 = run PRNG benchmark and exit (nogui-friendly) */
|
||||
double prng_bench_seconds; /* seconds per PRNG (default e.g. 0.25 for auto, 1.0 for manual) */
|
||||
char* banner; // The product banner shown on the top line of the screen.
|
||||
void* method; // A function pointer to the wipe method that will be used.
|
||||
char logfile[FILENAME_MAX]; // The filename to log the output to.
|
||||
@@ -68,7 +73,11 @@ typedef struct
|
||||
int verbose; // Make log more verbose
|
||||
int PDF_enable; // 0=PDF creation disabled, 1=PDF creation enabled
|
||||
int PDF_preview_details; // 0=Disable preview Org/Cust/date/time before drive selection, 1=Enable Preview
|
||||
int PDF_toggle_host_info; // 0=Disable visibility of host Info on PDF. UUID & S/N
|
||||
int PDFtag; // Enable display of hostID, such as UUID or serial no. on PDF report.
|
||||
nwipe_verify_t verify; // A flag to indicate whether writes should be verified.
|
||||
nwipe_io_mode_t io_mode; // Global runtime I/O mode selection (auto/direct/cached), note in auto mode each
|
||||
// drive may use a different I/O mode if directIO isn't supported on a given drive.
|
||||
} nwipe_options_t;
|
||||
|
||||
extern nwipe_options_t nwipe_options;
|
||||
|
||||
810
src/pass.c
810
src/pass.c
File diff suppressed because it is too large
Load Diff
639
src/prng.c
639
src/prng.c
@@ -27,19 +27,32 @@
|
||||
#include "isaac_rand/isaac64.h"
|
||||
#include "alfg/add_lagg_fibonacci_prng.h" //Lagged Fibonacci generator prototype
|
||||
#include "xor/xoroshiro256_prng.h" //XORoshiro-256 prototype
|
||||
#include "aes/aes_ctr_prng.h" // AES-NI prototype
|
||||
|
||||
nwipe_prng_t nwipe_twister = { "Mersenne Twister (mt19937ar-cok)", nwipe_twister_init, nwipe_twister_read };
|
||||
nwipe_prng_t nwipe_twister = { "Mersenne Twister", nwipe_twister_init, nwipe_twister_read };
|
||||
|
||||
nwipe_prng_t nwipe_isaac = { "ISAAC (rand.c 20010626)", nwipe_isaac_init, nwipe_isaac_read };
|
||||
nwipe_prng_t nwipe_isaac64 = { "ISAAC-64 (isaac64.c)", nwipe_isaac64_init, nwipe_isaac64_read };
|
||||
nwipe_prng_t nwipe_isaac = { "ISAAC", nwipe_isaac_init, nwipe_isaac_read };
|
||||
nwipe_prng_t nwipe_isaac64 = { "ISAAC-64", nwipe_isaac64_init, nwipe_isaac64_read };
|
||||
|
||||
/* ALFG PRNG Structure */
|
||||
nwipe_prng_t nwipe_add_lagg_fibonacci_prng = { "Lagged Fibonacci generator",
|
||||
nwipe_prng_t nwipe_add_lagg_fibonacci_prng = { "Lagged Fibonacci",
|
||||
nwipe_add_lagg_fibonacci_prng_init,
|
||||
nwipe_add_lagg_fibonacci_prng_read };
|
||||
/* XOROSHIRO-256 PRNG Structure */
|
||||
nwipe_prng_t nwipe_xoroshiro256_prng = { "XORoshiro-256", nwipe_xoroshiro256_prng_init, nwipe_xoroshiro256_prng_read };
|
||||
|
||||
/* AES-CTR-NI PRNG Structure */
|
||||
nwipe_prng_t nwipe_aes_ctr_prng = { "AES-CTR (Kernel)", nwipe_aes_ctr_prng_init, nwipe_aes_ctr_prng_read };
|
||||
|
||||
static const nwipe_prng_t* all_prngs[] = {
|
||||
&nwipe_twister,
|
||||
&nwipe_isaac,
|
||||
&nwipe_isaac64,
|
||||
&nwipe_add_lagg_fibonacci_prng,
|
||||
&nwipe_xoroshiro256_prng,
|
||||
&nwipe_aes_ctr_prng,
|
||||
};
|
||||
|
||||
/* Print given number of bytes from unsigned integer number to a byte stream buffer starting with low-endian. */
|
||||
static inline void u32_to_buffer( u8* restrict buffer, u32 val, const int len )
|
||||
{
|
||||
@@ -260,7 +273,7 @@ int nwipe_isaac64_read( NWIPE_PRNG_READ_SIGNATURE )
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* EXPERIMENTAL implementation of Lagged Fibonacci generator a lot of random numbers */
|
||||
/* Implementation of Lagged Fibonacci generator a lot of random numbers */
|
||||
int nwipe_add_lagg_fibonacci_prng_init( NWIPE_PRNG_INIT_SIGNATURE )
|
||||
{
|
||||
if( *state == NULL )
|
||||
@@ -274,7 +287,7 @@ int nwipe_add_lagg_fibonacci_prng_init( NWIPE_PRNG_INIT_SIGNATURE )
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* EXPERIMENTAL implementation of XORoroshiro256 algorithm to provide high-quality, but a lot of random numbers */
|
||||
/* Implementation of XORoroshiro256 algorithm to provide high-quality, but a lot of random numbers */
|
||||
int nwipe_xoroshiro256_prng_init( NWIPE_PRNG_INIT_SIGNATURE )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Initialising XORoroshiro-256 PRNG" );
|
||||
@@ -320,7 +333,7 @@ int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE )
|
||||
u8* restrict bufpos = buffer;
|
||||
size_t words = count / SIZE_OF_XOROSHIRO256_PRNG;
|
||||
|
||||
/* Loop to fill the buffer with blocks directly from the XORoroshiro256 algorithm */
|
||||
/* Loop to fill the buffer with blocks directly from the XORoshiro256 algorithm */
|
||||
for( size_t ii = 0; ii < words; ++ii )
|
||||
{
|
||||
xoroshiro256_genrand_uint256_to_buf( (xoroshiro256_state_t*) *state, bufpos );
|
||||
@@ -340,3 +353,615 @@ int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE )
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the AES-CTR PRNG state for this thread.
|
||||
*
|
||||
* @details
|
||||
* Initializes the thread-local PRNG based on the supplied seed and resets the
|
||||
* ring-buffer prefetch cache. The underlying AES-CTR implementation uses a
|
||||
* persistent AF_ALG operation socket per thread, opened lazily by
|
||||
* aes_ctr_prng_init(). The public state only stores a 128-bit counter while
|
||||
* the kernel keeps the expanded AES key schedule.
|
||||
*
|
||||
* @param[in,out] state Pointer to an opaque PRNG state handle. If `*state` is
|
||||
* `NULL`, this function allocates it with `calloc()`.
|
||||
* @param[in] seed Seed material (must contain at least 32 bytes).
|
||||
* @param[in] ... Remaining parameters as defined by NWIPE_PRNG_INIT_SIGNATURE.
|
||||
*
|
||||
* @note
|
||||
* The ring is intentionally left empty to keep init fast. Callers may choose to
|
||||
* "prefill" by invoking refill_stash_thread_local(*state, SIZE_OF_AES_CTR_PRNG)
|
||||
* once to amortize first-use latency for tiny reads.
|
||||
*
|
||||
* @retval 0 Success.
|
||||
* @retval -1 Allocation or initialization failure (already logged).
|
||||
*/
|
||||
|
||||
/*
|
||||
* High-throughput wrapper with a thread-local ring-buffer prefetch
|
||||
* ----------------------------------------------------------------
|
||||
* This glue layer implements NWIPE_PRNG_INIT / NWIPE_PRNG_READ around the
|
||||
* persistent kernel-AES PRNG. It maintains a lock-free, thread-local ring
|
||||
* buffer ("stash") that caches keystream blocks produced in fixed-size chunks
|
||||
* (SIZE_OF_AES_CTR_PRNG; e.g., 16 KiB or 256 KiB).
|
||||
*
|
||||
* Rationale:
|
||||
* - Nwipe frequently requests small slices (e.g., 32 B, 512 B, 4 KiB). Issuing
|
||||
* one kernel call per small read would be syscall- and copy-bound.
|
||||
* - By fetching larger chunks and serving small reads from the ring buffer,
|
||||
* we reduce syscall rate and memory traffic and approach memcpy-limited
|
||||
* throughput on modern CPUs with AES acceleration.
|
||||
*
|
||||
* Why a ring buffer (over a linear stash + memmove):
|
||||
* - No O(n) memmove() when the buffer fills with a tail of unread bytes.
|
||||
* - Constant-time head/tail updates via modulo arithmetic.
|
||||
* - Better cache locality and fewer TLB/cache misses; improved prefetching.
|
||||
*/
|
||||
|
||||
/** @def NW_THREAD_LOCAL
|
||||
* @brief Portable thread-local specifier for C11 and GNU C.
|
||||
*
|
||||
* The ring buffer and its indices are thread-local, so no synchronization
|
||||
* (locks/atomics) is required. Do not share this state across threads.
|
||||
*/
|
||||
#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L
|
||||
#define NW_THREAD_LOCAL _Thread_local
|
||||
#else
|
||||
#define NW_THREAD_LOCAL __thread
|
||||
#endif
|
||||
|
||||
/** @def NW_ALIGN
|
||||
* @brief Minimal alignment helper for hot buffers/structures.
|
||||
*
|
||||
* 64-byte alignment targets typical cacheline boundaries to reduce false
|
||||
* sharing and improve hardware prefetch effectiveness for linear scans.
|
||||
*/
|
||||
#if defined( __GNUC__ ) || defined( __clang__ )
|
||||
#define NW_ALIGN( N ) __attribute__( ( aligned( N ) ) )
|
||||
#else
|
||||
#define NW_ALIGN( N ) _Alignas( N )
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def STASH_CAPACITY
|
||||
* @brief Ring capacity in bytes (power-of-two; multiple of CHUNK).
|
||||
*
|
||||
* @details
|
||||
* Defaults to 1 MiB. Must be:
|
||||
* - a power of two (allows modulo via bitmask),
|
||||
* - a multiple of SIZE_OF_AES_CTR_PRNG, so each produced chunk fits whole.
|
||||
*
|
||||
* @note
|
||||
* Practical choices: 512 KiB … 4 MiB depending on CHUNK size and workload.
|
||||
* For SIZE_OF_AES_CTR_PRNG = 256 KiB, 1 MiB yields four in-flight chunks and
|
||||
* works well for nwipe’s small-read patterns.
|
||||
*/
|
||||
#ifndef STASH_CAPACITY
|
||||
#define STASH_CAPACITY ( 1u << 20 ) /* 1 MiB */
|
||||
#endif
|
||||
|
||||
#if defined( __STDC_VERSION__ ) && __STDC_VERSION__ >= 201112L
|
||||
_Static_assert( ( STASH_CAPACITY & ( STASH_CAPACITY - 1 ) ) == 0, "STASH_CAPACITY must be a power of two" );
|
||||
_Static_assert( ( STASH_CAPACITY % SIZE_OF_AES_CTR_PRNG ) == 0,
|
||||
"STASH_CAPACITY must be a multiple of SIZE_OF_AES_CTR_PRNG" );
|
||||
#endif
|
||||
|
||||
/** @brief Thread-local ring buffer storage for prefetched keystream. */
|
||||
NW_THREAD_LOCAL static unsigned char stash[STASH_CAPACITY] NW_ALIGN( 64 );
|
||||
|
||||
/**
|
||||
* @name Ring indices (thread-local)
|
||||
* @{
|
||||
* @var rb_head Next read position (consumer cursor).
|
||||
* @var rb_tail Next write position (producer cursor).
|
||||
* @var rb_count Number of valid bytes currently stored.
|
||||
*
|
||||
* @invariant
|
||||
* - 0 <= rb_count <= STASH_CAPACITY
|
||||
* - rb_head, rb_tail in [0, STASH_CAPACITY)
|
||||
* - (rb_tail - rb_head) mod STASH_CAPACITY == rb_count
|
||||
*
|
||||
* @warning
|
||||
* These variables are TLS and must not be accessed from or shared with other
|
||||
* threads. One PRNG instance per thread.
|
||||
* @}
|
||||
*/
|
||||
NW_THREAD_LOCAL static size_t rb_head = 0; /* next byte to read */
|
||||
NW_THREAD_LOCAL static size_t rb_tail = 0; /* next byte to write */
|
||||
NW_THREAD_LOCAL static size_t rb_count = 0; /* occupied bytes */
|
||||
|
||||
/**
|
||||
* @brief Free space available in the ring (bytes).
|
||||
* @return Number of free bytes (0 … STASH_CAPACITY).
|
||||
*/
|
||||
static inline size_t rb_free( void )
|
||||
{
|
||||
return STASH_CAPACITY - rb_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Contiguous readable bytes starting at @c rb_head (no wrap).
|
||||
* @return Number of contiguous bytes available to read without split memcpy.
|
||||
*/
|
||||
static inline size_t rb_contig_used( void )
|
||||
{
|
||||
size_t to_end = STASH_CAPACITY - rb_head;
|
||||
return ( rb_count < to_end ) ? rb_count : to_end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Contiguous writable bytes starting at @c rb_tail (no wrap).
|
||||
* @return Number of contiguous bytes available to write without wrap.
|
||||
*/
|
||||
static inline size_t rb_contig_free( void )
|
||||
{
|
||||
size_t to_end = STASH_CAPACITY - rb_tail;
|
||||
size_t free = rb_free();
|
||||
return ( free < to_end ) ? free : to_end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ensure at least @p need bytes are buffered in the ring.
|
||||
*
|
||||
* @details
|
||||
* Production model:
|
||||
* - The kernel PRNG produces keystream in fixed-size chunks
|
||||
* (SIZE_OF_AES_CTR_PRNG bytes; e.g., 16 KiB or 256 KiB).
|
||||
* - We only ever append *whole* chunks. If total free space is less than one
|
||||
* chunk, no production occurs (non-blocking style); the caller should first
|
||||
* consume data and try again.
|
||||
*
|
||||
* Wrap handling:
|
||||
* - Fast path: if a contiguous free region of at least one chunk exists at
|
||||
* @c rb_tail, generate directly into @c stash + rb_tail (zero extra copies).
|
||||
* - Wrap path: otherwise, generate one chunk into a small temporary buffer and
|
||||
* split-copy into [rb_tail..end) and [0..rest). This case is infrequent and
|
||||
* still cheaper than memmoving ring contents.
|
||||
*
|
||||
* @param[in] state Pointer to the AES-CTR state (per-thread).
|
||||
* @param[in] need Minimum number of bytes the caller would like to have ready.
|
||||
*
|
||||
* @retval 0 Success (or no space to produce yet).
|
||||
* @retval -1 PRNG failure (aes_ctr_prng_genrand_128k_to_buf() error).
|
||||
*
|
||||
* @warning
|
||||
* Thread-local only. Do not call concurrently from multiple threads that share
|
||||
* the same TLS variables.
|
||||
*/
|
||||
static int refill_stash_thread_local( void* state, size_t need )
|
||||
{
|
||||
while( rb_count < need )
|
||||
{
|
||||
/* Not enough total free space for a full CHUNK → let the caller read first. */
|
||||
if( rb_free() < SIZE_OF_AES_CTR_PRNG )
|
||||
break;
|
||||
|
||||
size_t cf = rb_contig_free();
|
||||
if( cf >= SIZE_OF_AES_CTR_PRNG )
|
||||
{
|
||||
/* Fast path: generate straight into the ring. */
|
||||
if( aes_ctr_prng_genrand_128k_to_buf( (aes_ctr_state_t*) state, stash + rb_tail ) != 0 )
|
||||
return -1;
|
||||
rb_tail = ( rb_tail + SIZE_OF_AES_CTR_PRNG ) & ( STASH_CAPACITY - 1 );
|
||||
rb_count += SIZE_OF_AES_CTR_PRNG;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Wrap path: temporary production, then split-copy. */
|
||||
unsigned char tmp[SIZE_OF_AES_CTR_PRNG];
|
||||
if( aes_ctr_prng_genrand_128k_to_buf( (aes_ctr_state_t*) state, tmp ) != 0 )
|
||||
return -1;
|
||||
size_t first = STASH_CAPACITY - rb_tail; /* bytes to physical end */
|
||||
memcpy( stash + rb_tail, tmp, first );
|
||||
memcpy( stash, tmp + first, SIZE_OF_AES_CTR_PRNG - first );
|
||||
rb_tail = ( rb_tail + SIZE_OF_AES_CTR_PRNG ) & ( STASH_CAPACITY - 1 );
|
||||
rb_count += SIZE_OF_AES_CTR_PRNG;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------- PRNG INIT ---------------- */
|
||||
|
||||
/**
|
||||
* @brief Thread-local initialization wrapper around @c aes_ctr_prng_init().
|
||||
*
|
||||
* @param[in,out] state Address of the caller’s PRNG state pointer. If `*state`
|
||||
* is `NULL`, this function allocates one `aes_ctr_state_t`.
|
||||
* @param[in] seed Seed descriptor as defined by NWIPE_PRNG_INIT_SIGNATURE.
|
||||
*
|
||||
* @retval 0 Success.
|
||||
* @retval -1 Allocation or backend initialization failure (logged).
|
||||
*
|
||||
* @note
|
||||
* Resets the ring buffer to empty. Consider a one-time prefill if your workload
|
||||
* is dominated by tiny reads.
|
||||
*/
|
||||
int nwipe_aes_ctr_prng_init( NWIPE_PRNG_INIT_SIGNATURE )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_NOTICE, "Initializing AES-CTR PRNG (thread-local ring buffer)" );
|
||||
|
||||
if( *state == NULL )
|
||||
{
|
||||
*state = calloc( 1, sizeof( aes_ctr_state_t ) );
|
||||
if( *state == NULL )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_FATAL, "calloc() failed for PRNG state" );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int rc = aes_ctr_prng_init(
|
||||
(aes_ctr_state_t*) *state, (unsigned long*) seed->s, seed->length / sizeof( unsigned long ) );
|
||||
if( rc != 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "aes_ctr_prng_init() failed" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reset ring to empty. */
|
||||
rb_head = rb_tail = rb_count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------- PRNG READ ---------------- */
|
||||
|
||||
/**
|
||||
* @brief Copy @p count bytes of keystream into @p buffer.
|
||||
*
|
||||
* @details
|
||||
* Strategy:
|
||||
* - If the request is "large" (>= CHUNK) and the ring is empty, use the
|
||||
* direct-fill fast path and generate full CHUNKs directly into the output
|
||||
* buffer to avoid an extra memcpy.
|
||||
* - Otherwise, serve from the ring:
|
||||
* * Ensure at least one byte is available via @c refill_stash_thread_local
|
||||
* (non-blocking; production occurs only if one full CHUNK fits).
|
||||
* * Copy the largest contiguous block starting at @c rb_head.
|
||||
* * Opportunistically prefetch when sufficient free space exists to keep
|
||||
* latency low for upcoming small reads.
|
||||
*
|
||||
* @param[out] buffer Destination buffer to receive keystream.
|
||||
* @param[in] count Number of bytes to generate and copy.
|
||||
* @param[in] ... Remaining parameters as defined by NWIPE_PRNG_READ_SIGNATURE.
|
||||
*
|
||||
* @retval 0 Success (exactly @p count bytes written).
|
||||
* @retval -1 Backend/IO failure (already logged).
|
||||
*
|
||||
* @warning
|
||||
* Per-thread API: do not share this state across threads.
|
||||
*/
|
||||
int nwipe_aes_ctr_prng_read( NWIPE_PRNG_READ_SIGNATURE )
|
||||
{
|
||||
unsigned char* out = buffer;
|
||||
size_t bytes_left = count;
|
||||
|
||||
/* Fast path: for large reads, bypass the ring if currently empty.
|
||||
* Generate full CHUNKs directly into the destination to save one memcpy. */
|
||||
while( bytes_left >= SIZE_OF_AES_CTR_PRNG && rb_count == 0 )
|
||||
{
|
||||
if( aes_ctr_prng_genrand_128k_to_buf( (aes_ctr_state_t*) *state, out ) != 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PRNG direct fill failed" );
|
||||
return -1;
|
||||
}
|
||||
out += SIZE_OF_AES_CTR_PRNG;
|
||||
bytes_left -= SIZE_OF_AES_CTR_PRNG;
|
||||
}
|
||||
|
||||
/* General path: serve from ring, refilling as needed. */
|
||||
while( bytes_left > 0 )
|
||||
{
|
||||
/* Ensure at least one byte is available for tiny reads. Refill only
|
||||
* produces if a full CHUNK fits; otherwise we try again once consumer
|
||||
* progress frees enough space. */
|
||||
if( rb_count == 0 )
|
||||
{
|
||||
if( refill_stash_thread_local( *state, 1 ) != 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PRNG refill failed" );
|
||||
return -1;
|
||||
}
|
||||
if( rb_count == 0 )
|
||||
continue; /* still no room for a CHUNK yet */
|
||||
}
|
||||
|
||||
/* Copy the largest contiguous span starting at rb_head. */
|
||||
size_t avail = rb_contig_used();
|
||||
size_t take = ( bytes_left < avail ) ? bytes_left : avail;
|
||||
|
||||
memcpy( out, stash + rb_head, take );
|
||||
|
||||
rb_head = ( rb_head + take ) & ( STASH_CAPACITY - 1 );
|
||||
rb_count -= take;
|
||||
out += take;
|
||||
bytes_left -= take;
|
||||
|
||||
/* Opportunistic prefetch to hide latency of future small reads. */
|
||||
if( rb_free() >= ( 2 * SIZE_OF_AES_CTR_PRNG ) )
|
||||
{
|
||||
if( refill_stash_thread_local( *state, SIZE_OF_AES_CTR_PRNG ) != 0 )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PRNG opportunistic refill failed" );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* PRNG benchmark / auto-selection core
|
||||
* ---------------------------------------------------------------------- */
|
||||
|
||||
static double nwipe_prng_monotonic_seconds( void )
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime( CLOCK_MONOTONIC, &ts );
|
||||
return (double) ts.tv_sec + (double) ts.tv_nsec / 1000000000.0;
|
||||
}
|
||||
|
||||
/* einfacher LCG zum Seed-Befüllen – nur für Benchmark, kein Kryptokram */
|
||||
static void nwipe_prng_make_seed( unsigned char* seed, size_t len )
|
||||
{
|
||||
unsigned long t = (unsigned long) time( NULL );
|
||||
unsigned long x = ( t ^ 0xA5A5A5A5UL ) + (unsigned long) (uintptr_t) seed;
|
||||
|
||||
for( size_t i = 0; i < len; i++ )
|
||||
{
|
||||
x = x * 1664525UL + 1013904223UL;
|
||||
seed[i] = (unsigned char) ( ( x >> 16 ) & 0xFF );
|
||||
}
|
||||
}
|
||||
|
||||
static void* nwipe_prng_alloc_aligned( size_t alignment, size_t size )
|
||||
{
|
||||
void* p = NULL;
|
||||
if( posix_memalign( &p, alignment, size ) != 0 )
|
||||
return NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* --- live spinner state ------------------------------------------------- */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int enabled;
|
||||
int is_tty;
|
||||
int spin_idx;
|
||||
double last_tick;
|
||||
|
||||
/* message currently shown with spinner (includes current PRNG label) */
|
||||
char msg[160];
|
||||
} nwipe_prng_live_t;
|
||||
|
||||
static void nwipe_prng_live_clear_line( nwipe_prng_live_t* live )
|
||||
{
|
||||
if( !live || !live->enabled || !live->is_tty )
|
||||
return;
|
||||
|
||||
printf( "\r\033[K" );
|
||||
fflush( stdout );
|
||||
}
|
||||
|
||||
static void nwipe_prng_live_set_msg_for_prng( nwipe_prng_live_t* live, const nwipe_prng_t* prng )
|
||||
{
|
||||
if( !live || !live->enabled )
|
||||
return;
|
||||
|
||||
if( prng && prng->label )
|
||||
snprintf( live->msg, sizeof( live->msg ), "Testing %s performance", prng->label );
|
||||
else
|
||||
snprintf( live->msg, sizeof( live->msg ), "Testing PRNG performance" );
|
||||
}
|
||||
|
||||
static void nwipe_prng_live_render_spinner( nwipe_prng_live_t* live, int advance )
|
||||
{
|
||||
static const char spin[] = "-\\|/";
|
||||
if( !live || !live->enabled || !live->is_tty )
|
||||
return;
|
||||
|
||||
if( advance )
|
||||
live->spin_idx = ( live->spin_idx + 1 ) & 3;
|
||||
|
||||
printf( "\r\033[K%s %c", live->msg, spin[live->spin_idx] );
|
||||
fflush( stdout );
|
||||
}
|
||||
|
||||
static void nwipe_prng_live_tick_if_due( nwipe_prng_live_t* live, double now )
|
||||
{
|
||||
if( !live || !live->enabled || !live->is_tty )
|
||||
return;
|
||||
|
||||
/* ~10 Hz */
|
||||
if( ( now - live->last_tick ) >= 0.10 )
|
||||
{
|
||||
live->last_tick = now;
|
||||
nwipe_prng_live_render_spinner( live, /*advance=*/1 );
|
||||
}
|
||||
}
|
||||
|
||||
/* --- bench one PRNG (unchanged except it ticks spinner) ----------------- */
|
||||
|
||||
static void nwipe_prng_bench_one( const nwipe_prng_t* prng,
|
||||
nwipe_prng_bench_result_t* out,
|
||||
void* io_buf,
|
||||
size_t io_block,
|
||||
double seconds_per_prng,
|
||||
nwipe_prng_live_t* live )
|
||||
{
|
||||
void* state = NULL;
|
||||
|
||||
unsigned char seedbuf[4096];
|
||||
nwipe_entropy_t seed;
|
||||
seed.s = (u8*) seedbuf;
|
||||
seed.length = sizeof( seedbuf );
|
||||
nwipe_prng_make_seed( seedbuf, sizeof( seedbuf ) );
|
||||
|
||||
out->prng = prng;
|
||||
out->mbps = 0.0;
|
||||
out->seconds = 0.0;
|
||||
out->bytes = 0;
|
||||
out->rc = 0;
|
||||
|
||||
int rc = prng->init( &state, &seed );
|
||||
if( rc != 0 )
|
||||
{
|
||||
out->rc = rc;
|
||||
if( state )
|
||||
free( state );
|
||||
return;
|
||||
}
|
||||
|
||||
const double t0 = nwipe_prng_monotonic_seconds();
|
||||
double now = t0;
|
||||
|
||||
if( live && live->enabled && live->is_tty )
|
||||
{
|
||||
live->last_tick = now;
|
||||
nwipe_prng_live_render_spinner( live, /*advance=*/0 );
|
||||
}
|
||||
|
||||
while( ( now - t0 ) < seconds_per_prng )
|
||||
{
|
||||
rc = prng->read( &state, io_buf, io_block );
|
||||
if( rc != 0 )
|
||||
{
|
||||
out->rc = rc;
|
||||
break;
|
||||
}
|
||||
|
||||
out->bytes += (unsigned long long) io_block;
|
||||
now = nwipe_prng_monotonic_seconds();
|
||||
|
||||
/* rotate cursor while running */
|
||||
nwipe_prng_live_tick_if_due( live, now );
|
||||
}
|
||||
|
||||
out->seconds = now - t0;
|
||||
if( out->rc == 0 && out->seconds > 0.0 )
|
||||
out->mbps = ( (double) out->bytes / ( 1024.0 * 1024.0 ) ) / out->seconds;
|
||||
|
||||
if( state )
|
||||
free( state );
|
||||
}
|
||||
|
||||
/* --- benchmark all with live current-PRNG spinner ----------------------- */
|
||||
|
||||
int nwipe_prng_benchmark_all_live( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count,
|
||||
int live_print )
|
||||
{
|
||||
if( results == NULL || results_count == 0 )
|
||||
return 0;
|
||||
|
||||
size_t max = sizeof( all_prngs ) / sizeof( all_prngs[0] );
|
||||
if( results_count < max )
|
||||
max = results_count;
|
||||
|
||||
void* io_buf = nwipe_prng_alloc_aligned( 4096, io_block_bytes );
|
||||
if( !io_buf )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_ERROR, "PRNG benchmark: unable to allocate %zu bytes buffer", io_block_bytes );
|
||||
return -1;
|
||||
}
|
||||
|
||||
nwipe_prng_live_t live;
|
||||
memset( &live, 0, sizeof( live ) );
|
||||
live.enabled = ( live_print ? 1 : 0 );
|
||||
live.is_tty = ( live.enabled ? ( isatty( fileno( stdout ) ) ? 1 : 0 ) : 0 );
|
||||
live.spin_idx = 0;
|
||||
live.last_tick = nwipe_prng_monotonic_seconds();
|
||||
snprintf( live.msg, sizeof( live.msg ), "Testing PRNG performance" );
|
||||
|
||||
for( size_t i = 0; i < max; i++ )
|
||||
{
|
||||
nwipe_prng_bench_result_t* r = &results[i];
|
||||
memset( r, 0, sizeof( *r ) );
|
||||
|
||||
const nwipe_prng_t* prng = all_prngs[i];
|
||||
|
||||
if( live.enabled )
|
||||
{
|
||||
if( live.is_tty )
|
||||
{
|
||||
/* single status line with spinner showing current PRNG */
|
||||
nwipe_prng_live_set_msg_for_prng( &live, prng );
|
||||
live.last_tick = nwipe_prng_monotonic_seconds();
|
||||
nwipe_prng_live_render_spinner( &live, /*advance=*/0 );
|
||||
}
|
||||
}
|
||||
|
||||
nwipe_prng_bench_one( prng, r, io_buf, io_block_bytes, seconds_per_prng, &live );
|
||||
|
||||
if( live.enabled && live.is_tty )
|
||||
nwipe_prng_live_clear_line( &live );
|
||||
|
||||
/* print result immediately after each PRNG */
|
||||
if( live.enabled )
|
||||
{
|
||||
if( r->rc == 0 )
|
||||
printf( "%-22s -> %8.1f MB/s\n", r->prng->label, r->mbps );
|
||||
else
|
||||
printf( "%-22s -> (failed: rc=%d)\n", r->prng->label, r->rc );
|
||||
fflush( stdout );
|
||||
}
|
||||
}
|
||||
|
||||
if( live.enabled && live.is_tty )
|
||||
nwipe_prng_live_clear_line( &live );
|
||||
|
||||
free( io_buf );
|
||||
return (int) max;
|
||||
}
|
||||
|
||||
/* keep old API intact (no live output) */
|
||||
int nwipe_prng_benchmark_all( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count )
|
||||
{
|
||||
return nwipe_prng_benchmark_all_live( seconds_per_prng, io_block_bytes, results, results_count, /*live_print=*/0 );
|
||||
}
|
||||
|
||||
/* --- select fastest: now uses live benchmark so printing is not delayed -- */
|
||||
|
||||
const nwipe_prng_t* nwipe_prng_select_fastest( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count )
|
||||
{
|
||||
/* live_print=1 => prints per PRNG immediately (plus spinner) */
|
||||
int n = nwipe_prng_benchmark_all_live( seconds_per_prng, io_block_bytes, results, results_count, /*live_print=*/1 );
|
||||
if( n <= 0 )
|
||||
return NULL;
|
||||
|
||||
const nwipe_prng_t* best = NULL;
|
||||
double best_mbps = 0.0;
|
||||
|
||||
for( int i = 0; i < n; i++ )
|
||||
{
|
||||
if( results[i].rc == 0 && results[i].mbps > best_mbps )
|
||||
{
|
||||
best_mbps = results[i].mbps;
|
||||
best = results[i].prng;
|
||||
}
|
||||
}
|
||||
|
||||
if( best == NULL )
|
||||
{
|
||||
nwipe_log( NWIPE_LOG_WARNING, "Auto PRNG selection: no successful PRNG benchmark" );
|
||||
printf( "Auto PRNG selection: no successful PRNG benchmark\n" );
|
||||
fflush( stdout );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "Selected PRNG: %s\n", best->label );
|
||||
fflush( stdout );
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
42
src/prng.h
42
src/prng.h
@@ -45,6 +45,38 @@ typedef struct
|
||||
nwipe_prng_read_t read; // Read data from the prng.
|
||||
} nwipe_prng_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const nwipe_prng_t* prng;
|
||||
double mbps;
|
||||
double seconds;
|
||||
unsigned long long bytes;
|
||||
int rc;
|
||||
} nwipe_prng_bench_result_t;
|
||||
|
||||
/* Existing API (kept for compatibility: no live output) */
|
||||
int nwipe_prng_benchmark_all( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count );
|
||||
|
||||
/* New API: live output (spinner + per-PRNG immediate prints)
|
||||
* live_print:
|
||||
* 0 = behave like old benchmark (silent, just fills results[])
|
||||
* 1 = print "Analysing PRNG performance:" immediately, rotate cursor,
|
||||
* print "Testing <PRNG>..." before each PRNG, and print result right after.
|
||||
*/
|
||||
int nwipe_prng_benchmark_all_live( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count,
|
||||
int live_print );
|
||||
|
||||
const nwipe_prng_t* nwipe_prng_select_fastest( double seconds_per_prng,
|
||||
size_t io_block_bytes,
|
||||
nwipe_prng_bench_result_t* results,
|
||||
size_t results_count );
|
||||
|
||||
/* Mersenne Twister prototypes. */
|
||||
int nwipe_twister_init( NWIPE_PRNG_INIT_SIGNATURE );
|
||||
int nwipe_twister_read( NWIPE_PRNG_READ_SIGNATURE );
|
||||
@@ -63,6 +95,10 @@ int nwipe_add_lagg_fibonacci_prng_read( NWIPE_PRNG_READ_SIGNATURE );
|
||||
int nwipe_xoroshiro256_prng_init( NWIPE_PRNG_INIT_SIGNATURE );
|
||||
int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE );
|
||||
|
||||
/* AES-CTR-NI prototypes. */
|
||||
int nwipe_aes_ctr_prng_init( NWIPE_PRNG_INIT_SIGNATURE );
|
||||
int nwipe_aes_ctr_prng_read( NWIPE_PRNG_READ_SIGNATURE );
|
||||
|
||||
/* Size of the twister is not derived from the architecture, but it is strictly 4 bytes */
|
||||
#define SIZE_OF_TWISTER 4
|
||||
|
||||
@@ -76,4 +112,10 @@ int nwipe_xoroshiro256_prng_read( NWIPE_PRNG_READ_SIGNATURE );
|
||||
/* Size of the XOROSHIRO-256 is not derived from the architecture, but it is strictly 32 bytes */
|
||||
#define SIZE_OF_XOROSHIRO256_PRNG 32
|
||||
|
||||
/* AES-CTR generation chunk size: fixed 128 KiB (not architecture-dependent) */
|
||||
#define SIZE_OF_AES_CTR_PRNG ( 128 * 1024 )
|
||||
|
||||
/* Thread-local prefetch ring buffer capacity: 1 MiB */
|
||||
#define STASH_CAPACITY ( 1024 * 1024 )
|
||||
|
||||
#endif /* PRNG_H_ */
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
* used by configure to dynamically assign those values
|
||||
* to documentation files.
|
||||
*/
|
||||
const char* version_string = "0.38";
|
||||
const char* version_string = "0.40";
|
||||
const char* program_name = "nwipe";
|
||||
const char* author_name = "Martijn van Brummelen";
|
||||
const char* email_address = "git@brumit.nl";
|
||||
const char* years = "2024";
|
||||
const char* years = "2025";
|
||||
const char* copyright = "Copyright Darik Horn <dajhorn-dban@vanadac.com>\n\
|
||||
Andy Beverley <andy@andybev.com> Martijn van Brummelen <martijn@brumit.nl>\n\
|
||||
Nick Law <shredos.eraser@gmail.com> (@PartialVolume) and others\n\
|
||||
This is free software; see the source for copying conditions.\n\
|
||||
There is NO warranty; not even for MERCHANTABILITY or FITNESS\n\
|
||||
FOR A PARTICULAR PURPOSE.\n";
|
||||
const char* banner = "nwipe 0.38";
|
||||
const char* banner = "nwipe 0.40";
|
||||
|
||||
Reference in New Issue
Block a user