5
\$\begingroup\$

Your job is to encrypt a string using a number key.

Your program will receive 2 strings: one containing a sequence of characters to encrypt, and another one containing an even amount of digits (1-9, never 0).

The encrypting will work like this:

  1. take the string to be encrypt and decode it to binary (ISO-8859-1)
  2. for every pair of numbers x and y in the number string do: for every character in the string of zeroes and ones, starting from the first one, if the bit is 0 toggle the xth bit in front of the bit you are reading right now. If the bit is 1, toggle the yth bit in front of the bit you are reading right now. If the bit to be toggled is out of bounds, do not toggle anything.

Other than that:

This is a code golf challenge, shortest code wins. Standard Loopholes apply.

Here's a more graphic explanation of what happens:

inputs: "a", "23"
"a" =BIN> "01100001"
"01100001" //first bit is a 0, get the first number in the key, 2.
 - ^       //toggle the bit 2 steps in front
"01000001" //next bit is 1, get second number in the key, 3.
  -  ^     //toggle the bit 3 steps in front
"01001001" //0 bit, toggle the bit that is 2 steps in front.
   - ^
"01000001" //etc
    - ^
"01000101"
     - ^
"01000111"
      -  ^ //no change, out of bounds.
"01000111"
       -  ^
"01000111"
        -  ^
"01000111" =ASCII> "G"

So: a, 23 = G. Then, running G, 23 should be a because this process is perfectly reversible. To decrypt a string encrypted using key 12438135, reverse the ordering of the pairs, 35814312 and run the encrypted string through a similar program that performs the operations in reverse order (starting at the end, approaching the beginning) using that key. (thanks to Martin Büttner for pointing that out)

Good luck and have fun! (My first challenge, tell me if i missed anything :))

\$\endgroup\$
5
  • \$\begingroup\$ I've reordered and made some minor changes to make this less unclear, but it's still not entirely clear to me what the process is. "Extract the bits of each character": in what encoding? I would guess ISO-8859-1, but you should make that explicit. "Form a long string of zeroes and ones, appending each character" using big-endian? "For every pair of numbers": that is, splitting the number string into two, not advancing for (i=0; i+1 < length; i++)? What does "in front of" mean? And why does the code block only use x and never y? \$\endgroup\$ Sep 17, 2014 at 14:55
  • \$\begingroup\$ I guess i wasn't quite clear with the question, I'll edit it now. \$\endgroup\$
    – vero
    Sep 17, 2014 at 14:56
  • 1
    \$\begingroup\$ I will create a base program now to illustrate what the program should accomplish \$\endgroup\$
    – vero
    Sep 17, 2014 at 15:12
  • \$\begingroup\$ Btw, I don't think this process is reversible, because some of the toggles depend on earlier toggles. So the first affected bit will return to what it was before. But once you get there, it will cause the toggling of a different bit than during the encoding. My program below yields n if I feed it G. \$\endgroup\$ Sep 17, 2014 at 15:38
  • \$\begingroup\$ You are right. To reverse the process you have to run the program again, but instead of starting at the first bit, start at the last one and work backwards to cancel out the flipped bits. Good catch. I will edit the question when i get back from school. \$\endgroup\$
    – vero
    Sep 17, 2014 at 16:17

4 Answers 4

3
\$\begingroup\$

Ruby, 230 191 181 178 175 173 171 159 bytes

i,n=$*
s=i.gsub(/./){|c|"%08b"%c.ord}
n.scan(/(.)(.)/){s.size.times{|i|s[j=i+(s[i]==?0?$1:$2).to_i]&&s[j]=s[j]==?0??1:?0}}
$><<s.gsub(/.{8}/){|c|c.to_i(2).chr}

It's just a very naive implementation of the spec. Input via command-line arguments, output to STDOUT.

# Read ARGV into i and n
i,n = $*
# Turn each character into a string of eight 0s and 1s using a format string.
s = i.gsub(/./){|c|"%08b"%c.ord}
# For each pair of characters in n
n.scan(/(.)(.)/){
    # For each index into s
    s.size.times{|i|
        # Determine the index that is to be toggled. $1 and $2 refer to the captures 
        # of /(.)(.)/.
        j=i+(s[i]==?0?$1:$2).to_i
        # Using short-circuiting, toggle s[j] if it isn't nil.
        s[j]&&s[j]=s[j]==?0??1:?0
    }
}
# Convert each group of 8 characters back to a single character and print
$><<s.gsub(/.{8}/){|c|c.to_i(2).chr}
\$\endgroup\$
1
\$\begingroup\$

CJam, 53 bytes

SSl++256b2bl2/{Em<_,E-{_0=P=~_2$=!t1m<}*}fPE>2b256b:c

Reads two lines of input: the string, followed by the key.

Try it online.

For decryption, this code can be used:

SSl++256b2bl2/W%{1m>_,E-{_0=P=~_2$=!t1m>}*Dm>}fPE>2b256b:c

Example run

$ LANG=en_US cjam enc.cjam # First two lines are input.
codegolf
345678
q¼M³)6¯J

How it works

CJam's built-in base conversion discards leading zeros, so we start by prepending two spaces to the input string. Since a space character is [0 0 1 0 0 0 0 0], this prepends 14 zeros to the bit encoding for the input string.

After each transformation, we rotate the array to the left to avoid adding indexes, skipping the extra 14 bytes from the spaces. This way, toggling bits "out of bounds" affects only those 14 bits, which are discarded after the last toggle.

SSl++       " Read one line of input (plaintext) and prepend two spaces. ";
256b2b      " String ↦ byte array ↦ bit array. ";
l2/         " Read one line of input (key) and split it in chunks of length 2. ";
{           " For each digit pair P: ";
  Em<       " Rotate the array 14 places to the left. ";
  _,E-      " Push the arrays length minus 14. ";
  {         " Do the following that many times: ";
    _0=P=~  " Push the digit of P that corresponds to the first bit of the array. ";
    _2$=    " Retrieve the corresponding bit from the array. ";
    !t      " Compute its logical NOT and modify the array at the given position. ";
    1m<     " Rotate the array one place to the left. ";
  }*        " ";
}fP         " ";
E>          " Discard the first 14 bits of the array. ";
2b256b:c    " Bit array ↦ byte array ↦ string. ";
\$\endgroup\$
2
  • \$\begingroup\$ Wow this is pretty cool. Padding the array and discarding out of bounds modifications is quite ingenious. It doesn't really have to be spaces though, right? Its discarded at the end anyways. \$\endgroup\$
    – vero
    Sep 17, 2014 at 22:44
  • \$\begingroup\$ Almost any other character would have worked (null bytes, e.g., would accomplish nothing), but S is a convenient shorthand for " ". The only thing that varies the the shift amount. \$\endgroup\$
    – Dennis
    Sep 17, 2014 at 22:49
0
\$\begingroup\$

GolfScript, 45

~2/{\{8{(..2\?3$&!!4$=48-- 2\?@\^\.}do;}%\;}/

This takes arguments from stdin in the form of two strings (e.g. "a""23") and outputs to stdout.

Test it online here

\$\endgroup\$
1
  • \$\begingroup\$ For "codegolf""345678", it produces an incorrect result. With the official interpreter, it just crashes (Rational can't be coerced into Rational for bitwise arithmetic). \$\endgroup\$
    – Dennis
    Sep 21, 2014 at 1:24
0
\$\begingroup\$

JavaScript 243

function E(s,k,d){for (var o="",a=0,b=0;a<s.length;a++,b%=k.length){
var j,i=8,z,n=s.charCodeAt(a),x=parseInt(k.charAt(b++)),y=parseInt(k.charAt(b++))
for (;i--;)j=d?7-i:i,z=j-(n&(1<<j)?y:x),n=z>0?n^=1<<z:n
o+=String.fromCharCode(n)
}return o}

Example:

var s0 = "codegolf";
var k = "5556";
var s1 = E(s0,k);
var s2 = E(s1,k,true);
console.log(s0+"\n"+s1+"\n"+s2)

Output:

codegolf
ekbaakjb
codegolf
\$\endgroup\$

Your Answer

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

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