15
\$\begingroup\$

Write a program or function (or set of programs/functions) to encrypt and decrypt data given the following specification:

Encryption

  1. Calculate an XOR hash of the input by XOR-ing every byte with each-other.

  2. XOR every byte of the input by this hash.

  3. Shift the result four bits left.

  4. Pad the left side with the first four bits of the XOR hash.

  5. Pad the right side with the last four bits of the XOR hash.

Example

  • Given input: "G0lf" (0x47306C66)

  • Calculate XOR hash: 0x47 ^ 0x30 ^ 0x6C ^ 0x66 = 0x7D

  • XOR every byte by hash: 0x3A4D111B

  • Expected result (after shift and pad): "s¤Ñ\x11½" (0x73A4D111BD)

Rules

  • Your program/function can input/output of whatever type makes sense in your golfing language of choice (String, Byte Array, etc) as long as the input/output are the actual bytes. For example, you may not output a hexidecimal string.

  • Encryption and decryption can be separated into separate programs (score will be the their combined size) or a single one. Single methods can take an argument for whether it should encrypt or decrypt.

  • Input for encryption can be expected to be at least 1 byte in size.

  • Input for decryption can be expected to be at least 2 bytes.

  • Non-printable bytes do not need to be escaped in the output.

\$\endgroup\$
  • 1
    \$\begingroup\$ Could an decimal array be used as an output form? \$\endgroup\$ – ɐɔıʇǝɥʇuʎs Apr 25 '15 at 17:56
  • \$\begingroup\$ @ɐɔıʇɥʇuʎs It would be acceptable to take input and output as an array of integers to represent bytes. \$\endgroup\$ – nderscore Apr 25 '15 at 19:01
  • \$\begingroup\$ Is there a maximum input length (for example, 14 bytes (56 bits), so that the final result fits in a 64-bit integer)? \$\endgroup\$ – Doorknob Apr 25 '15 at 20:00
  • 1
    \$\begingroup\$ Just a note: From a cryptography point of view this is not an encryption, as it has no key (or a 0-bit key). \$\endgroup\$ – Paŭlo Ebermann Apr 25 '15 at 21:21
  • 1
    \$\begingroup\$ I'm just waiting for someone to post something about never rolling your own encryption, ignoring the site this is on... \$\endgroup\$ – user253751 Apr 25 '15 at 22:02
9
\$\begingroup\$

CJam, 28 + 27 = 55 bytes

For each part I'm presenting one program which expects input/output to be in the form of an integer array and one which uses a string. The above byte count is for the integer array version, but the linked script and the explanation are for the string-based version (which can be used to test the example given in the question).

Encryption

q~_:^_GbYUe[\@f^Gfbe_*2/Gfbp
q:i_:^_GbYUe[\@f^Gfbe_*2/Gfb:c

Decryption

q~{_G/\G%}%)\(G*@+\2/Gfbf^p
q:i{_G/\G%}%)\(G*@+\2/Gfbf^:c

Here is a test script which does a full round trip and prints the encrypted code before doing the decryption again.

Explanation

q:i_:^_GbYUe[\@f^Gfbe_*2/Gfb:c
q:i                            e# Read the input and convert characters to byte values.
   _:^                         e# Duplicate and fold XOR onto the characters to get 
                               e# the hash.
      _Gb                      e# Duplicate and convert to base 16 to get nibbles.
         YUe[                  e# Pad to width 2 with zeroes.
             \@                e# Pull up other copy and integer array.
               f^              e# XOR each integer with the hash.
                 Gfbe_         e# Convert each result to base 16 and flatten that.
                      *        e# Join the hash nibbles with this array.
                       2/      e# Split into pairs.
                         Gfb   e# Interpret each pair as base 16.
                            :c e# Convert each integer to a character.

q:i{_G/\G%}%)\(G*@+\2/Gfbf^:c
q:i                            e# Read the input and convert characters to byte values.
   {      }%                   e# Map this block onto each byte.
    _G/\G%                     e# Get the two base 16 digits individually.
            )\(                e# Slice off the last and first nibble.
               G*@+\           e# Combine into byte (the hash) and swap with array.
                    2/Gfb      e# Split array into pairs and interpret each as base 16.
                         f^    e# XOR each with the hash.
                           :c  e# Convert each integer to a character.
\$\endgroup\$
6
\$\begingroup\$

CJam, 36 + 34 = 70 bytes

A bit different approach using binary forms

Encrypter:

q_:^:Hf^H+{i2b8Ue[}%)4/~@\]e_8/2fb:c

How it works:

q_:^                                  e# Read input as string, copy and XOR all the chars
    :Hf^                              e# Store the XOR in H and XOR each char with H
        H+                            e# Append H to the char array
          {       }%                  e# On each of the elements in the array
           i2b                        e# Convert the ASCII value to binary
              8Ue[                    e# Pad with 0 so that the length is 8
                    )                 e# Pop out the last array element, which is H
                     4/~@\            e# Put first 4 bits of H before the input array
                                      e# And rest 4 after it
                          ]e_8/       e# Flatten everything into a single array and group
                                      e# into pieces of 8 bits
                               2fb:c  e# Convert each 8 bit part to integer and then to
                                      e# its character form

Decrypter:

q{i2b8Ue[4/~}%)\(@+2b\:+8/2fb\f^:c

How it works:

q{          }%                      e# For each character of the input string
  i2b                               e# Convert to ASCII code and then to its binary form
     8Ue[                           e# Pad with enough 0 so that length is 8 bit
         4/~                        e# Split into parts of 4 and unwrap
              )\(@+                 e# Take out the first and last 4 bit group and join
                                    e# them together to get the XOR Hash H
                   2b\              e# Convert H to decimal form and swap to put the
                                    e# remaining converted input array on top
                      :+8/          e# Join all bits together and split into groups of 8
                          2fb       e# Convert each 8 but group to decimal form
                             \f^    e# Swap to put H on top and XOR each number with H
                                :c  e# Get character from each of the ASCII value

Try the encrypter and decrypter online

\$\endgroup\$
6
\$\begingroup\$

Pyth, 69 bytes

Ksm>+0jCd16_2zJ?,hKeKQmxFdCcK2=KsmmxFkC,dJc?tPKQK2smCid16c?KQ++hJKeJ2

This combines encryption and decryption, simply add a 0 as argument for encryption or a 1 for decryption. The reason for this is simple. Converting strings into bits (or 4 bit integer) or the reverse is really really long in Pyth. By combining the both functions into one program, I can save a lot of bytes.

Online demonstrations: Encryption and Decryption.

Explanation:

The first part converts the input into a list of 4-bit integer (each char get converted into 2 4-bit integer) and stores it in K.

  m          z   map each character d of input (=z) to:
       Cd            the ascii-value of d
      j  16          convert the result into base 16
   >+0     _2        insert a zero to the front and take the last 2 values
                     (so that each char gets mapped to exactly 2 numbers)
Ks               chain all these tuples and assign them to K

The second part determines the hash values and stores them in J. If Q==0 it computes them by xor, otherwise it takes the first and last value of K.

 ?     Q           ... if Q (=second input) else ...
  ,hKeK            [K[0], K[-1]]
        m   CcK2   map each d of zipped(K chopped into pairs) to:
                   [zipped(...) gives me 2 lists, one with the values of the even indices, and one with the odd indices]
         xFd           fold the list d by xor
J                  store the result in J (this is the hash value)

The next part does the xor using the hash values. When Q == 0 it is performed on the complete list K, otherwise only on the list K without the first and last value.

=KsmmxFkC,dJc?tPKQK2
             ?tPKQK    K[1:-1] if Q else K 
   m        c      2   map each d of [... chopped into pairs] to:
    m   C,dJ              map each pair k of zip(d,J) to:
     xFk                     apply xor to the 2 values in k
=Ks                    chain all these tuples and assign them to K

And the last part converts K back to chars:

smCid16c?KQ++hJKeJ2
        ?KQ++hJKeJ    K if Q else J[0] + K + J[1]
 m     c          2   map each pair of [... chopped into pairs] to:
   id16                  convert d into a single integer
  C                      convert to char
s                     join all chars and print
\$\endgroup\$
0
\$\begingroup\$

Javascript (ES6) 83 + 73 = 156

Both functions take input as and ouput an array of numbers to represent bytes.

Encrypt 85 84 83

E=s=>s.concat((h=s.reduce((x,y)=>x^y))<<4&240^h).map(x=>a<<4&240|(a=x^h)>>4,a=h>>4)

Decrypt 75 73

D=s=>s.map(x=>(a<<4&240|(a=x)>>4)^h,h=(a=s.shift())&240|s[~-s.length]&15)

Demonstration (Firefox only)

E=s=>s.concat((h=s.reduce((x,y)=>x^y))<<4&240^h).map(x=>a<<4&240|(a=x^h)>>4,a=h>>4)
D=s=>s.map(x=>(a<<4&240|(a=x)>>4)^h,h=(a=s.shift())&240|s[~-s.length]&15)

toHexString = x=>'0x'+x.map(y=>y.toString(16)).join('')

input = [...'G0lf'].map(x=>x.charCodeAt());
document.write('Input: ' + toHexString(input) + '<br />');

encrypted = E(input);
document.write('Encrypted: ' + toHexString(encrypted) + '<br />');

decrypted = D(encrypted);
document.write('Decrypted: ' + toHexString(decrypted) + '<br />');


Using Strings 131 + 129 = 260

And just for fun... here's some versions which use strings for input/output instead.

E=(s,h=0)=>[for(x of s)(h^=y=x.charCodeAt(),y)].concat(h<<4&240^h).map(x=>String.fromCharCode(a<<4&240|(a=x^h)>>4),a=h>>4).join('')

D=s=>(s=[s.charCodeAt(j=i)for(i in s)]).map(x=>String.fromCharCode((a<<4&240|(a=x)>>4)^h),h=(a=s.shift())&240|s[~-j]&15).join('')

E('G0lf') // 's¤Ñ\x11½'
D('s¤Ñ\x11½') // 'G0lf'
\$\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.