17
\$\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\$
13
  • 1
    \$\begingroup\$ Could an decimal array be used as an output form? \$\endgroup\$ Apr 25, 2015 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, 2015 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, 2015 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\$ Apr 25, 2015 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, 2015 at 22:02

4 Answers 4

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\$
0
\$\begingroup\$

Jelly, 25 + 17 = 42 bytes

Oµ^^/;^/b⁴Ż€-.ịⱮµṪjFs2ḅ⁴Ọ

Try it online!

Ob⁴FµḢ,Ṫ,s2$ḅ⁴^/Ọ

Try it online!

Full round trip.

+6 bytes correcting a bug which looks like it also plagues Martin's CJam answer

How they work

Encryption

Oµ^^/;^/b⁴Ż€-.ịⱮµṪjFs2ḅ⁴Ọ - Main link. Takes S on the left
O                         - Convert S to char codes; Call this O
 µ                        - Discard S and use O as the left argument
   ^/                     - Reduce O by XOR to get the hash, X
  ^                       - XOR each element in O by X
      ^/                  - X
     ;                    - Concatenate to the end of O
         ⁴                - 16
        b                 - Convert each to base 16
          Ż€              - Prepend a 0 to each
               Ɱ          - For each:
            -.ị           -   Get the -0.5th index
                              This returns the last 2 elements of the list
                              As the ị atom returns [floor(x)ị, ceil(x)ị] for
                              non-integer x, and uses 1-indexing. Therefore,
                              -.ị = -0.5ị = [floor(-.)ị, ceil(-.)ị]
                                  = [-ị, 0ị] = last 2 elements

                              For a integer < 16, b⁴ returns a singleton list
                              Otherwise, it'll return (here) a pair
                              We then prepend 0 so each list has either 2 or 3 elements
                              and ignore the leading 0 if it already had 2 elements

                            Call this list of nibbles N
                            Currently, N = [[a, b], [c, d], ..., [x, y]]
                            where [a, b] are the nibbles of the XOR'ed elements
                            and [x, y] are the nibbles of X

                µ         - Discard O and use N as the left argument
                 Ṫ        - Remove [x, y] from N
                  j       - Join [x, y] with N, yielding [x, [a, b], ..., y]
                   F      - Flatten; [x, a, b, c, d, ..., y]
                    s2    - Split into pairs; [[x, a], [b, c], ...]
                      ḅ⁴  - Convert each from base 16
                        Ọ - Convert back to characters

Decryption

Ob⁴FµḢ,Ṫ,s2$ḅ⁴^/Ọ - Main link. Takes S on the left
O                 - Convert to char codes
 b⁴               - Convert to hex
   F              - Flatten; Call this list of hex bytes H = [x, a, b, ..., y]
    µ             - Discard S and use H as the left argument
     Ḣ            - Remove the first element of H, x
       Ṫ          - Remove the final element of H, y
      ,           - Pair; [x, y]
           $      - Group the previous two links as a monad f(H):
         s2       -   Split into pairs
        ,         - Pair with [x, y]; [[x, y], [[a, b], ...]]
            ḅ⁴    - Convert each from hex
              ^/  - Reduce by XOR
                Ọ - Convert into chararacters
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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