tperdue has asked for the wisdom of the Perl Monks concerning the following question:

Is there a quick and easy way to modulo-2 add two binary files together to get a third file?

Replies are listed 'Best First'.
Re: Mod-2 add 2 binary files
by samtregar (Abbot) on May 18, 2004 at 22:26 UTC
    How about:

    open(FILE3, '>file3') or die $!; print FILE3 ((`cat file1` + `cat file2`) % 2); close FILE3;

    Not what you meant? Yeah, I figured. Maybe you could slow down and explain exactly what you're trying to do?

    -sam

Re: Mod-2 add 2 binary files
by zude (Scribe) on May 18, 2004 at 22:20 UTC
    By "Mod-2 add" do you mean exclusive OR? Are the files the same length?

    +++++++++ In theory, theory describes reality, but in reality it doesn't.

      Yes I do mean exclusive OR. The 2 binary files are in fact the same file. So I should have said I want to exclusive OR a file to itself giving me a new file that is the original file with a Delta-1 on it.
        OIC. If this is what you want to do, then just:
        ln -s /dev/null cipher.txt
        Because X ^ X == 0.

        ~%{${@_[0]}}->{0}&&+++ NO CARRIER

Re: Mod-2 add 2 binary files
by dave_the_m (Monsignor) on May 18, 2004 at 22:23 UTC
    Is there a quick and easy way to modulo-2 add two binary files together to get a third file?
    How do you mean exactly? As in treat two identically sized files of size N bytes as two 8N-bit numbers, which added together give an (8N+1)-bit result? or treat them as an array of 8-bit (or 32-bit) numbers, and add each byte or word separately, modulo 2^8 (or 2^32)?

    And what to you want it to do when the files are different sizes? And how do you want to handle endian issues?

Re: Mod-2 add 2 binary files
by sgifford (Prior) on May 18, 2004 at 23:37 UTC
    As zude said, modulo-2 addition of binary numbers is the same as exclusive-OR. So you can read a character at a time from the two files, convert each character to a number with ord, XOR these numbers together with ^, convert back to a character with chr, then print that to the output file. You may be get faster performance using pack and unpack to deal with units larger than a single character.
Re: Mod-2 add 2 binary files
by thor (Priest) on May 19, 2004 at 03:09 UTC
    It sounds like you're trying to implement a one-time pad. Let me demonstrate with a couple of one-liners:
    thulben@bravo:~/perl 125> perl -le 'print "A" ^ "C"' | perl -nle 'print $_^"C"' A thulben@bravo:~/perl 126> perl -le 'print "A" ^ "C"' | perl -nle 'print $_^"A"' C thulben@bravo:~/perl 127>
    Basically, (X xor Y) xor X = Y for all X,Y. And, because the xor operation is equivalent to addition modulo 2 (or, as the algebraist might say "addition in the finite field with two elements"), this works. A canned solution? Here's a quicky:
    use warnings; use strict; my $cipher = shift; my $key = shift; open( my $cipher_fh, $cipher ) or die $!; open( my $key_fh, $key ) or die $!; my $buffer_length = 4_096; while ( sysread( $cipher_fh, my $cipher_text, $buffer_length, ) && sysread( $key_fh, my $key_text, $buffer_length, ) ) { print $cipher_text ^ $key_text; }
    I don't do a lot of error checking here, so there is room to improve.

    thor

    p.s. In case it wasn't obvious, you'll need to redirect the output from the script that I provided above: it prints to standard out
      Close but no cigar, you can't xor arbitrary strings that way. The following will cipher STDIN to STDOUT using the contents of the keyfile on the command line. Assumption is that the key file is much smaller than memory (maybe 5% or less):
      use warnings; use strict; @ARGV or die "Usage: cipher keyfile < source > dest\n"; open K,shift or die "Can't open keyfile: $!\n"; undef $/; my $k = <K>; close K; # slurp length $k or die "error reading key file\n"; while (read STDIN, my $t, length $k) { my @k = unpack "C*", $k; print pack "C*", map { $_^shift @k } unpack "C*", $t; }
      Now let's try it:
      # make a key file, ok for demo purposes $ perl -e 'print chr int rand 256 for 0..10000' > key.file # make a test source file $ ls -al /dev > plain.txt # encipher $ perl cipher.pl key.file < plain.txt > cipher.txt # decipher $ perl cipher.pl key.file < cipher.txt > deciphered.txt # verify $ diff -s plain.txt deciphered.txt Files plain.txt and deciphered.txt are identical


      ~%{${@_[0]}}->{0}&&+++ NO CARRIER
        I'm not sure that I understand why you can't bit-wise xor two strings:
        $foo = "baz"; $bar = "foo"; print unpack("B*", $foo); print unpack("B*", $bar); print unpack("B*", ($bar ^ $foo)); __END__ 011000100110000101111010 011001100110111101101111 000001000000111000010101
        Seems to do the right thing to me...

        thor