Securely Delete All Drive Data 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 &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;time.h&gt;
#include &lt;stdint.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;
#include &lt;openssl/sha.h&gt;

#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 &lt; 8; ++i)
    *a++ ^= *b++;
}

static void _cipher(RandomCtx* ctx, unsigned char* data, unsigned int len)
{
  unsigned int runs = len &gt;&gt; 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-&gt;state, data, 64);
    ++ctx-&gt;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)) &amp; ~(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, &amp;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) &gt;= (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(&amp;ctx, aligned, BLOCK_SIZE);
  }
}
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.