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;
    // 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);

    // move on
    data += 64;

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){
    return EXIT_FAILURE;

  // allocate memory enough
  char* raw = malloc(BLOCK_SIZE + ALIGNMENT - 1);
    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){
    return EXIT_FAILURE;

  // init random ctx
  RandomCtx ctx;
  if(sizeof ctx != read(urandom, &ctx, sizeof ctx)){
    return EXIT_FAILURE;

  // init buffer
  if(BLOCK_SIZE != read(urandom, aligned, BLOCK_SIZE)){
    return EXIT_FAILURE;

  // we are done with 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;
      size_t secs = time(NULL) - ti0;      
      printf("Total: %d GiB in %zu secs. %.2f MiB/s\n", gib, 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\$ Nov 21 '16 at 18:42

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.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.