5
\$\begingroup\$

8-bit XOR encryption takes a single byte and XORs every byte in the file by that byte. Here is a reference program in C++:

/*xorcipher.cpp
 *Encrypts a file using 8-bit xor encrytpion
 */

#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;

int main(int argc, char** argv){
   ifstream* in = NULL;
   ofstream* out = NULL;

   if(argc < 3){
      cerr << "Usage: xor <key> <filename...>" << endl;
      exit(1);
   }

   int key = atoi(argv[1]);

   for(int x = 2; x < argc; x++){
      in = new ifstream(argv[x], ios::binary);
      if(!in->good()){
         cerr << "Error reading from " << argv[x] << endl;
         exit(1);
      }

      //Get file size
      in->seekg(0, ios::end);
      int size = in->tellg();
      in->seekg(0, ios::beg);

      //Allocate memory
      int* data = new int[size];

      int c = 0;
      for(int y = 0; (c = in->get()) != EOF; y++)
         data[y] = c ^ key;

      in->close();
      delete in;
      in = NULL;

      out = new ofstream(argv[x], ios::binary);
      if(!out->good()){
         cerr << "Error writing to " << argv[x] << endl;
         exit(1);
      }

      for(int y = 0; y < size; y++)
         out->put(data[y]);

      out->flush();
      out->close();
      delete out;
      out = NULL;

      delete[] data;
      data = NULL;
   }
   cout << "Operation complete" << endl;

   return 0;
}

The shortest answer (whitespace doesn't count) that produces the same end result will be selected. It does not have to store everything in memory at once or open and close every file multiple times. It can do anything as long as a file encrypted with the reference program can be decrypted with the answer, and vice versa.


Edit: The above program is not a specification. The only requirement is that answers should be able to decrypt a file encrypted with the above program, and the above program should be able to decrypt a file encrypted with the answer. I'm sorry if that was unclear.

The above program can be compiled with:

g++ xorcipher.cpp -o ~/bin/xor

It can be run like this:

xor <key> <filename...>

For example, if the key is 42 and the file is alpha.txt, you would use:

xor 42 alpha.txt
\$\endgroup\$
  • 5
    \$\begingroup\$ A 10-line specification which gives acceptance criteria is a lot more useful than a 60-line program in a language which not everyone knows. \$\endgroup\$ – Peter Taylor Dec 20 '12 at 7:58
  • 10
    \$\begingroup\$ You know that saying 'whitespace doesn't count' means there's going to be a 0-character solution in Whitespace, right? \$\endgroup\$ – marinus Dec 20 '12 at 17:22
  • 1
    \$\begingroup\$ Are alternate input formats, like streaming in the data, allowed? \$\endgroup\$ – moonheart08 May 1 '18 at 18:17
  • \$\begingroup\$ Yeah, it would be real nice if this didn't have the requirement for file I/O... \$\endgroup\$ – Unrelated String Apr 18 at 7:39

11 Answers 11

4
\$\begingroup\$

PHP 40 38 bytes

<?for(;;)echo@~fgetc(STDIN)^~$argv[1];

And updated version. Now handles all inputs correctly, and 2 bytes shorter as well.

Sample usage:

$ more in.dat
This is a test.
This is only a test.

$ php xor-cipher.php A < in.dat > out.dat

$ more out.dat
§)(2a(2a a5$25oK§)(2a(2a./-8a a5$25o

$ php xor-cipher.php A < out.dat
This is a test.
This is only a test.

Perl 30 bytes

$k=pop;s/./print$&^$k/egsfor<>

I/O is the same as for the PHP solution above.

\$\endgroup\$
  • \$\begingroup\$ Does it work if the output contains a zero character? (e.g. encrypt This is a test with the key T)? \$\endgroup\$ – ugoren Dec 20 '12 at 19:25
  • \$\begingroup\$ The Perl solution will work for all cases. The PHP will terminate prematurely if the generated character becomes a 0 (not character 0, literally a 0). I'll update my answer. \$\endgroup\$ – primo Dec 20 '12 at 20:01
6
\$\begingroup\$

Brainfuck - 269

,>,[<[>>+>+<<<-]>>>[<<<+>>>-]>>>++++++++[-<<<<<[->>+<<[->>->+<]>>[->>>
>+<<]<<<<]>>>[-<<<+>>>]<<[->+<[->->+>>>>>]>[->>>>>+>>]<<<<<<<<]>>[-<<+
>>]>>>[->>+<<]>[>[-<->]<[->+<]]>[[-]<<<[->+>-<<]>[-<+>]+>+++++++[-<[->
>++<<]>>[-<<+>>]<]<[->>>>+<<<<]>>]<<<]>>>>>.[-]<<<<<<<<<<,]
\$\endgroup\$
4
\$\begingroup\$

C, 61 59 bytes

Quite simple, and very similar to primo's solution.
First parameter is the key, in decimal, reads stdin, writes stdout.
The only tricky part is using ~getchar, so EOF becomes 0 and terminates the loop, then printing c^~key, where the additional ~ undoes the previous one.

main(c,v)int*v;{
    while(c=~getchar())putchar(c^~atoi(v[1]));
}
\$\endgroup\$
  • \$\begingroup\$ I like the bit inversion trick. Would ~c=getchar() also work (thereby saving the second inversion)? \$\endgroup\$ – primo Dec 20 '12 at 9:00
  • \$\begingroup\$ @primo, ~(c=getchar()) works, but the parentheses are needed. The second inversion saves them. \$\endgroup\$ – ugoren Dec 20 '12 at 9:01
4
\$\begingroup\$

The Powder Toy, 598 bytes, 8 particles

Only 8 particles and O(n) speed. :) (This is unsurprisingly difficult in TPT for most tasks unless you know subframe really well; I do not.)

This creation takes input at a rate of 8 bits per frame, with a 8 bit word size. If you input the xor byte 3 times at once (24bit), then you can process 3 bytes of the stream per frame.

Reversible xxd dump:

00000000: 4f50 5331 5d04 9960 151d 0c00 425a 6839  OPS1]..`....BZh9
00000010: 3141 5926 5359 5cdf 0ea9 006c 37ff ffff  1AY&SY\....l7...
00000020: f553 0252 11d7 423f 27df b6bf f7df e340  .S.R..B?'......@
00000030: 0200 4000 0400 1050 0008 0000 2002 0840  ..@....P.... ..@
00000040: 01fc 69b5 3508 44d1 0068 0034 1ea0 6800  ..i.5.D..h.4..h.
00000050: 0d03 4034 6800 000d 0814 d4fd 2626 4c89  ..@4h.......&&L.
00000060: a68d 0190 0000 d343 4069 9003 4d03 8686  .......C@i..M...
00000070: 4d34 34c8 d0d3 2320 c8c8 d0c8 0c4d 1934  M44...# .....M.4
00000080: 0193 2310 c244 9232 86a6 327a 4191 a260  ..#..D.2..2zA..`
00000090: 0046 2604 6260 8600 d263 4dbb b2e2 d1e3  .F&.b`...cM.....
000000a0: 21f5 cd39 7920 2062 4360 0924 1e6c 4021  !..9y  bC`.$.l@!
000000b0: 2224 22bb f2b3 9fbd 77d2 c6ad dc4e 4b3c  "$".....w....NK<
000000c0: 9fac 12d3 9ad6 6cae 6940 2409 08b2 9961  ......l.i@$....a
000000d0: a30e 9a64 b599 68b6 e10a 87ac c869 64a6  ...d..h......id.
000000e0: 6977 e649 2102 4227 62ce 6214 0641 cc41  iw.I!.B'b.b..A.A
000000f0: b575 205e 255c 520c 4603 15cc a222 4325  .u ^%\R.F...."C%
00000100: 1df7 2106 df63 c319 3bfd d191 25a8 0020  ..!..c..;...%.. 
00000110: 0484 3ab5 62ab ef4d 0902 4236 d9d3 eb8d  ..:.b..M..B6....
00000120: 23f1 3ee4 8113 30c0 6435 d122 2572 033d  #.>...0.d5."%r.=
00000130: 9edb f17a ac03 636c 60c0 82b9 1226 125c  ...z..cl`....&.\
00000140: 20c0 8855 972a ac26 833a 8466 8143 208a   ..U.*.&.:.f.C .
00000150: 4c9d 811d 9ec1 d800 356e ad3f 9b51 e0a0  L.......5n.?.Q..
00000160: 8a96 54ff 891d c761 71c3 3618 5844 f4f2  ..T....aq.6.XD..
00000170: 5453 ac54 d94a b86d 8261 11c9 3b70 4222  TS.T.J.m.a..;pB"
00000180: 9bfb 2445 4443 f202 f004 5b14 e01d 1696  ..$EDC....[.....
00000190: e06d 7348 e262 981e 847f 9e4d ba42 9040  .msH.b.....M.B.@
000001a0: d740 45b1 8fd9 7a62 20b8 7850 a40b a2a1  .@E...zb .xP....
000001b0: 34a5 a790 c2ab 8af3 2bec d254 9b50 c39e  4.......+..T.P..
000001c0: 1b74 9ec7 3627 63b2 f9da f832 09a4 9406  .t..6'c....2....
000001d0: 135c 78e3 0a29 a9ac 991a 44da a6d9 e38c  .\x..)....D.....
000001e0: 4886 f6db 9a65 3ce9 bd32 b3a9 8522 a053  H....e<..2...".S
000001f0: 5ee2 a930 527b 8dd6 f7b5 b5a9 15d7 b771  ^..0R{.........q
00000200: 8d2f bace a196 d95b f48c a895 05a1 0402  ./.....[........
00000210: c2c3 48cc 8e90 3567 0b0c acab 0eb2 1ce2  ..H...5g........
00000220: 80e9 40e7 1432 82fe b141 4412 b86b e551  ..@..2...AD..k.Q
00000230: 33bf 6160 a733 e306 cca3 5479 bf93 9a21  3.a`.3....Ty...!
00000240: 8921 0024 2342 f402 4090 8f91 7724 5385  .!.$#B..@...w$S.
00000250: 0905 cdf0 ea90

I/O

This creation takes the byte to XOR data by in particle #0, and the data stream at particle #4. Output is given by any particle at X 501. Once the stream is over it decodes the last byte(s) forever.

The XOR byte must have the 30th bit set, and the data byte must NOT have the 30th bit set. This is due to the fact that a input of 0 will cause no output if this is not done in this manner (TPT filt logic restriction, cannot be worked around)

What does it look like?

[TODO]

How does it work?

[Also TODO. Sorry.]

\$\endgroup\$
  • \$\begingroup\$ Looking forward to seeing a visualization of this code in action. \$\endgroup\$ – Jonathan Frech May 13 '18 at 17:58
  • \$\begingroup\$ @JonathanFrech Sorry to disappoint you, it's not the most intresting thing ever. FILT has a builtin XOR. Most of the particles are overhead for shooting rays. \$\endgroup\$ – moonheart08 May 14 '18 at 15:51
  • \$\begingroup\$ Hm. Would still be cool to see it in action ... \$\endgroup\$ – Jonathan Frech May 14 '18 at 19:57
2
\$\begingroup\$

Python, 78 chars

from sys import argv as v

print ''.join(chr(ord(v[1])^ord(i)) for i in open(v[2]).read())

Must be run like:

python solution.py <key> <input_file> > <output_file>

Where solution.py is the python file that contains the above code, key is the single byte to XOR, input_file is the file to be encrypted and output_file will contain the decrypted output.

I am not sure if it matches your solution (as per "...that produces the same end result will be selected") since your example doesn't seem to produce output. However, it does successfully decrypt an encrypted file with the same key byte.

Since whitespace doesn't count, I used the following code to count chars:

>>> with open('solution.py') as f:
...     text = f.read()
... 
>>> len(text) - text.count('\n') - text.count(' ') # There are no tabs in this code
78
\$\endgroup\$
2
\$\begingroup\$

Python 75

import sys
for j in sys.stdin:print''.join(chr(int(sys.argv[1])^ord(i))for i in j),

The hardest requirement was not to keep the whole file in memory.

Usage:

python xor.py 123 <file.in >file.out
\$\endgroup\$
  • \$\begingroup\$ I only said it didn't have to keep the file in memory. This means it could. \$\endgroup\$ – ctype.h Dec 20 '12 at 19:16
2
\$\begingroup\$

GolfScript, 16 characters

"#{$*}"~{^}+%:n;

Usage is as given above - the key (numeric) passed as parameter while input/output is through console:

golfscript.rb xor.gs 42 <in.txt >out.txt

Edit: The issues with the trailing newline are now solved.

\$\endgroup\$
1
\$\begingroup\$

Postscript 101 67

(]){exch def}def
ARGUMENTS aload pop(w)file/O](r)file/I]cvi/K]{O I read not{exit}if K xor write}loop

The 67 byte version is equivalent to the above with all executable system names replaced by binary tokens. It is produced as output by this program (which shows a hex representation).

(xorb.ps)(w)file
<
    285D297B923E92337D9233
    415247554D454E54539202927528772992412F4F5D
    28722992412F495D922C2F4B5D
    7B4F2049927B92707B92407D92544B92C192BC7D9265
>
writestring

Invoke with ghostscript's argument-processing option --.

josh@Z1 ~
$ gs -dNODISPLAY -- xorw.ps
GPL Ghostscript 9.06 (2012-08-08)
Copyright (C) 2012 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.

josh@Z1 ~
$ xxd xorb.ps
0000000: 285d 297b 923e 9233 7d92 3341 5247 554d  (]){.>.3}.3ARGUM
0000010: 454e 5453 9202 9275 2877 2992 412f 4f5d  ENTS...u(w).A/O]
0000020: 2872 2992 412f 495d 922c 2f4b 5d7b 4f20  (r).A/I].,/K]{O
0000030: 4992 7b92 707b 9240 7d92 544b 92c1 92bc  I.{.p{.@}.TK....
0000040: 7d92 65                                  }.e

josh@Z1 ~
$ gs -dNODISPLAY -- xorb.ps 42 xor.ps xor.ps.out
GPL Ghostscript 9.06 (2012-08-08)
Copyright (C) 2012 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.

josh@Z1 ~
$ gs -dNODISPLAY -- xorb.ps 42 xor.ps.out xor.rev
GPL Ghostscript 9.06 (2012-08-08)
Copyright (C) 2012 Artifex Software, Inc.  All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.

josh@Z1 ~
$ diff xor.ps xor.rev

josh@Z1 ~
$
\$\endgroup\$
  • \$\begingroup\$ Subtracting whitespace only gives me -10. :( \$\endgroup\$ – luser droog Dec 20 '12 at 6:53
  • \$\begingroup\$ There's only 1 byte of whitespace in the binary version, between the O and the I. \$\endgroup\$ – luser droog Dec 22 '12 at 6:53
0
\$\begingroup\$

q/kdb+, 35 bytes (41 including whitespace)

Solution:

{x 1:0b sv'(0b vs 4h$y)<>/:0b vs'read1 x}

Example:

q)read0 `:test.txt
"Hello World!"
"Welcome to PPCG :)"
q){x 1:0b sv'(0b vs 4h$y)<>/:0b vs'read1 x}[`:test.txt;"A"]
`:test.txt
q)read0 `:test.txt
"\t$--.a\026.3-%`"
"\026$-\".,$a5.a\021\021\002\006a{h"
q){x 1:0b sv'(0b vs 4h$y)<>/:0b vs'read1 x}[`:test.txt;"A"]
`:test.txt
q)read0 `:test.txt
"Hello World!"
"Welcome to PPCG :)"

Explanation:

Read in the file as a bytestream, convert to bitstream, convert key to bitstream and xor <> with each byte. Convert back to bytestream and write back to file:

{x 1:0b sv'(0b vs 4h$y)<>/:0b vs'read1 x} / ungolfed solution
{                                       } / lambda function taking x and y as implicit parameters
                                 read1 x  / read file x as bytestream
                           0b vs'         / convert each byte to bits (boolean list)
                       <>/:               / xor (<>) left with each-right (/:)
           (          )                   / do this together
                  4h$y                    / convert key to a byte
            0b vs                         / convert byte to bits (boolean list)
     0b sv'                               / convert each list of bits to a byte
 x 1:                                     / write bytes to file x 

Notes:

If we drop to the k prompt we have a 32 byte solution (33 inc whitespace), pretty much a straight port except we dont have <> for xor.

{x 1:0b/:'~(0b\:4h$y)=/:0b\:'1:x}

K4 Example:

  {x 1:0b/:'~(0b\:4h$y)=/:0b\:'1:x}[`:test.txt;"A"]
`:test.txt
  0:`:test.txt
,"\t$--.a\026.3-%`K\026$-\".,$a5.a\021\021\002\006K"
  {x 1:0b/:'~(0b\:4h$y)=/:0b\:'1:x}[`:test.txt;"A"]
`:test.txt
  0:`:test.txt
("Hello World!";"Welcome to PPCG")
\$\endgroup\$
0
\$\begingroup\$

Tcl, 120 bytes

set t [open t w]
lmap x [split [read [open $argv]] ""] {puts -nonewline $t [format %c [expr 255^[scan $x %c]]]}
close $t

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Chip, 39 bytes (62 incl. whitespace)

 A0   C2   E4   G6
a{L}bc{L}de{L}fg{L}hS!9
  1B   3D   5F   7H

Try it online!

The first byte of stdin is the key, all other bytes are the message.

The elements H-A are the eight bits of each input byte, h-a are the eight bits of each output byte, and 7-0 are the eight bits of the stack head.

On the first cycle, S!9 causes the input bits to be written to the stack (along with the pairs like A0, 1B...), and prevents any output.

On subsequent cycles, the input bits are xor'd with their corresponding stack bits to produce the output bits. A single bit is computed like this:

 A0
a{'

or like this:

,}b
1B

(which are just rotations of each other). Those two patterns can be made to overlap slightly, and we use four of each to cover all eight bits.

\$\endgroup\$

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.