When secure erasing a disk, how can I do a "bit-wise complement" with dd? (or with other UNIX tools?)

  • Haven't tried it (don't have a disk I want to erase atm), but have you tried unmounting the disk and running rm -P on the device? – alexis Dec 10 '13 at 18:38
  • Or you can copy /dev/random onto the disk, using dd... but rm -P is specially made for clearing traces. – alexis Dec 10 '13 at 18:39
  • Just wipe with random data in a single pass (shred -n 1 /dev/erase). – frostschutz Dec 10 '13 at 20:42
  • If you want to erase a disk securely, write zeroes. It's common sense that the new content shouldn't depend on the content you're trying to get rid of. A bitwise complement is especially silly since it's trivial to undo. – Gilles Dec 10 '13 at 23:27

I'm going to preface this by saying that bitwise complementing a disk is not a secure erase, because if someone knows what you did, all they have to do is reverse the process to restore the disk to its original state.

While bash has a complement operator, I don't think it can be used here because you can't work on raw bytes without some additional tool.

Here's a short C program that will serve as a bitwise complement pipeline:

#include <stdio.h>
#include <unistd.h>
#include <inttypes.h>

#define BUFSZ 4096

int main (void) {
    unsigned char buffer[BUFSZ];
    int i, check;
    uint64_t total = 0;

    while ((check = read(0, buffer, BUFSZ)) > 0) {
        for (i = 0; i < check; i++) buffer[i] = ~buffer[i];
        write(1, buffer, check);
        total += check;

    fprintf(stderr, "Bitcomp processed %lu bytes.\n", total);
    return 0;

Compile that:

gcc whatever.c -o bitcomp


dd if=something | ./bitcomp | dd of=something conv=notrunc

The conv=notrunc (no truncate) is necessary when writing to the same file that is being read from. You may not need it for a device. You can also use:

./bitcomp < something 1<> something

Where 1<> serves as the "no truncate" version of > (thanks Stephane Chazelas). Beware that the program reports to standard error (descriptor 2 for the shell), so don't redirect 2>&1 etc.

If you want to see what I mean by reversible, run it on a text file, then run it on the same text file again. It will be back the way it started.

  • 1
    Pass conv=notrunc to dd to avoid truncating the output file. Any way, you don't need dd here (especially not 2 invocations per sector!), you can use shell redirection. Doing one read and one write system call per byte is not terribly efficient. – Stéphane Chazelas Dec 10 '13 at 21:02
  • @StephaneChazelas : Thanks for the notrunc tip. You are right about the byte-by-byte methodology of course, I've fixed that. I can't get redirection of the form ./bitcomp < something > something to work though, that truncates too. – goldilocks Dec 11 '13 at 10:19
  • 1
    you need the <> redirection operator to get the equivalent of dd's conv=notrunc. See my answer – Stéphane Chazelas Dec 11 '13 at 10:23
  • wow, thanks! :) but how can someone be sure that this c code really does bit-wise complement? :O did you write it yourself or took it from somewhere? – gasko peter Dec 12 '13 at 14:05
  • I wrote it, so I guess you have to take my word for it, lol. Hopefully the read/write loop is clear enough even if you don't know C; the trick is buffer[i] = ~buffer[i] -- ~ is bitwise NOT, aka "one's complement": en.wikipedia.org/wiki/… as it says, not to be confused with logical negation (aka. "not"). So, e.g., ~0 is all bits set. – goldilocks Dec 12 '13 at 14:51

tr is the tool to transliterate bytes:

LC_ALL=C tr < file 1<> file '\0-\377' '\377\376\375\374\373\372\371\370\367\366\365\364\363\362\361\360\357\356\355\354\353\352\351\350\347\346\345\344\343\342\341\340\337\336\335\334\333\332\331\330\327\326\325\324\323\322\321\320\317\316\315\314\313\312\311\310\307\306\305\304\303\302\301\300\277\276\275\274\273\272\271\270\267\266\265\264\263\262\261\260\257\256\255\254\253\252\251\250\247\246\245\244\243\242\241\240\237\236\235\234\233\232\231\230\227\226\225\224\223\222\221\220\217\216\215\214\213\212\211\210\207\206\205\204\203\202\201\200\177\176\175\174\173\172\171\170\167\166\165\164\163\162\161\160\157\156\155\154\153\152\151\150\147\146\145\144\143\142\141\140\137\136\135\134\133\132\131\130\127\126\125\124\123\122\121\120\117\116\115\114\113\112\111\110\107\106\105\104\103\102\101\100\77\76\75\74\73\72\71\70\67\66\65\64\63\62\61\60\57\56\55\54\53\52\51\50\47\46\45\44\43\42\41\40\37\36\35\34\33\32\31\30\27\26\25\24\23\22\21\20\17\16\15\14\13\12\11\10\7\6\5\4\3\2\1\0'


LC_ALL=C tr < file 1<> file '\0-\377'  "$(awk '
  BEGIN{for (i=0;i<256;i++) printf "\\%o", 255-i}')"

But as others have pointed out, that does not make sense as a secure erase. First it is not secure as the original is easily derived, and then from a performance point of view, it's very inefficient on rotational storage as the data needs to be read, then a seek is necessary to rewrite the data over.


I think I would skip attempting to do this with dd myself and look to actual tools that provide these types of features. The 4 tools I'm familiar with that can do this are as follows:

  • nwipe
  • wipe
  • shred
  • BleachBit

Of the 4 I would look to nwipe which has an assortment of methods for securely wiping data from disks and/or partitions. I may also look to BleachBit too.

   ss of nwipe

NOTE: nwipe can be run from a ncurses TUI or from the command line directly.

excerpt from nwipe man page

   -m, --method=METHOD
          The wiping method (default: dodshort).
          dod522022m / dod       - 7 pass DOD 5220.22-M method
          dodshort / dod3pass    - 3 pass DOD method
          gutmann                - Peter Gutmann's Algorithm
          ops2                   - RCMP TSSIT OPS-II
          random / prng / stream - PRNG Stream
          zero / quick           - Overwrite with zeros

You can also tell it how many rounds to perform this:

   -r, --rounds=NUM
          Number  of  times  to  wipe the device using the selected method
          (default: 1)


$ nwipe -m dod /dev/sda1


Bitwise complement

If you take a look at the source code for nwipe you'll notice that the DoD 7 and DoD 3 methods both perform a bitwise complement.

DoD 7

    char dod [7];

    nwipe_pattern_t patterns [] =
            {  1, &dod[0] }, /* Pass 1: A random character.               */
            {  1, &dod[1] }, /* Pass 2: The bitwise complement of pass 1. */
            { -1, ""      }, /* Pass 3: A random stream.                  */
            {  1, &dod[3] }, /* Pass 4: A random character.               */
            {  1, &dod[4] }, /* Pass 5: A random character.               */
            {  1, &dod[5] }, /* Pass 6: The bitwise complement of pass 5. */
            { -1, ""      }, /* Pass 7: A random stream.                  */
            {  0, NULL   }

DoD 3

    char dod [3];

    nwipe_pattern_t patterns [] =
            {  1, &dod[0] }, /* Pass 1: A random character.               */
            {  1, &dod[1] }, /* Pass 2: The bitwise complement of pass 1. */
            { -1, ""      }, /* Pass 3: A random stream.                  */
            {  0, NULL   }

Researching this further it would appear that any software that purports to do either DoD 3 or 7 will perform this bitwise complement as part of the suit of passes that need to be performed for these particular wipe methods.

  • @MichaelKjörling - changed the G to a T 8-) – slm Dec 11 '13 at 12:56

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.