When giving away or selling a PC, one should be careful not to send personal data with it. I'm in such a situation and was using dd if=/dev/urandom of=/dev/sdb bs=8M
, but it was only averaging 22MB/s. Way too slow since I have to drop it at the post office next morning. So I decided to write my own and see what kinds of results I would get.
First I dropped /dev/urandom
in favor of hashing a secret key and feeding back the output as state. The second thing I did was to use O_DIRECT
to avoid buffering.
My program reports an average of 48MiB/s, much better than what I got with plain old dd
unless it has a bug.
#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <openssl/sha.h>
#define BLOCK_SIZE (1024 * 1024 * 8)
#define ALIGNMENT 512
typedef struct {
unsigned char state[64];
unsigned char key[32];
__uint128_t counter;
} RandomCtx;
static void _xor64(uint64_t* a, uint64_t* b)
{
for(int i = 0; i < 8; ++i)
*a++ ^= *b++;
}
static void _cipher(RandomCtx* ctx, unsigned char* data, unsigned int len)
{
unsigned int runs = len >> 6;
while(runs){
// get value to XOR
unsigned char x[64];
SHA512((unsigned char const*)ctx, sizeof(RandomCtx), x);
// XOR with data
_xor64((uint64_t*)data, (uint64_t*)x);
// feed the XORed result back as state and update counter
memcpy(ctx->state, data, 64);
++ctx->counter;
// move on
data += 64;
--runs;
}
}
int main(int argc, char* argv[])
{
if(2 != argc){
fprintf(stderr, "Usage: %s /dev/sdb\n", argv[0]);
return EXIT_FAILURE;
}
// open disk
int disk = open(argv[1], O_WRONLY|O_DIRECT);
if(-1 == disk){
perror("open()");
return EXIT_FAILURE;
}
// allocate memory enough
char* raw = malloc(BLOCK_SIZE + ALIGNMENT - 1);
if(!raw){
perror("malloc()");
return EXIT_FAILURE;
}
// align
unsigned char* aligned = (unsigned char*)
((size_t)(raw + (ALIGNMENT-1)) & ~(ALIGNMENT-1));
// open random source
int urandom = open("/dev/urandom", O_RDONLY);
if(-1 == urandom){
perror("open()");
return EXIT_FAILURE;
}
// init random ctx
RandomCtx ctx;
if(sizeof ctx != read(urandom, &ctx, sizeof ctx)){
perror("read()");
return EXIT_FAILURE;
}
// init buffer
if(BLOCK_SIZE != read(urandom, aligned, BLOCK_SIZE)){
perror("read()");
return EXIT_FAILURE;
}
// we are done with urandom
close(urandom);
// now run until there's no more space
time_t ti0 = time(NULL);
size_t to0 = 0;
size_t to1 = 0;
int gib = 0;
while(BLOCK_SIZE == write(disk, aligned, BLOCK_SIZE)){
to1 += BLOCK_SIZE;
if((to1 - to0) >= (1024*1024*1024)){
to0 = to1;
++gib;
size_t secs = time(NULL) - ti0;
printf("Total: %d GiB in %zu secs. %.2f MiB/s\n", gib, secs,
(double)gib*1024/secs);
}
_cipher(&ctx, aligned, BLOCK_SIZE);
}
}
-
1\$\begingroup\$ These days, you would probably be better off using whole-disk encryption. Then, to "wipe" the disk, you just throw away the key. It also protects you in case of hardware failure, where it's not even possible to execute the wipe. (Not that this helps you if you already have an unencrypted disk that you need to wipe.) \$\endgroup\$200_success– 200_success2016年11月21日 18:42:10 +00:00Commented Nov 21, 2016 at 18:42
1 Answer 1
If you actually need to securely wipe your non-SSD drive consider using shred, or look at some of the linked pages too.
Also I don't see much point in the pseudo-random data generation given that you just want to make the previous content unrecoverable. If you a want faster program you could probably use a faster hash too.
For the code I only want to note that write
might write less than the
specified number of bytes and that's not immediately an error.