Two-Many Outputs

17

3

The Challenge

I present to you another spy vs. spy challenge pitting obfuscators versus crackers. In this case, however, the datum to be protected is not an input but an output.

The rules of the challenge are simple. Write a routine with the following specifications:

  1. The routine may be written in any language but may not exceed 320 bytes.
  2. The routine must accept three 32-bit signed integers as inputs. It can take the form of a function that accepts 3 arguments, a function that accepts a single 3-element array, or a complete program that reads 3 integers from any standard input.
  3. The routine must output one signed 32-bit integer.
  4. Over all possible inputs, the routine must output between 2 and 1000 (inclusive) unique values. The number of unique values a routine can output is called its key.

As an example, the C program

int foo( int i1, int i2, int i3 ) {
    return 20 + (i1^i2^i3) %5;
}

has a key of 9, since it (hopefully) can only output the nine values 16,17,18,19,20,21,22,23, and 24.

Some additional limitations are as follows:

  1. The routine must be fully deterministic and time-invariant, returning identical outputs for identical inputs. The routine should make no call to pseudorandom number generators.
  2. The routine may not rely on "hidden variables" such as data in files, system variables, or esoteric language features. For example, routines should generally not refer to constants unless the constants are clearly defined in the code itself. Routines that rely on compiler quirks, outputs from mathematically undefined operations, arithmetic errors, etc. are also strongly discouraged. When in doubt, please ask.
  3. You (the coder) must know precisely how many unique outputs the routine can produce, and should be able to provide at least one input sequence that produces each output. (Since there can potentially be hundreds of unique outputs, this set would only ever be requested in the event your key is contested.)

Since this problem bares far less resemblance to classical encryption than the previous one, I expect it will be accessible to a broader audience.

The more creative, the better.

The Scoring

The shortest non-cracked submission(s) per byte count will be declared the winner(s).

If there's any confusion, please feel free to ask or comment.

The Counter-Challenge

All readers, including those who have submitted their own routines, are encouraged to "crack" submissions. A submission is cracked when its key is posted in the associated comments section. If a submission persists for 72 hours without being modified or cracked, it is considered "safe" and any subsequent success in cracking it will be ignored for sake of the contest.

Only one cracking attempt per submission per reader is permitted. For example, if I submit to user X: "your key is 20" and I'm wrong, user X will disclaim my guess as incorrect and I will no longer be able to submit additional guesses for that submission.

Cracked submissions are eliminated from contention (provided they are not "safe"). They should not be edited. If a reader wishes to submit a new routine, (s)he should do so in a separate answer.

A cracker's score is the number of submissions (either compliant or not) (s)he cracks. For crackers with identical counts, the ranking is determined by total byte count across all cracked submissions (the higher, the better).

The cracker(s) with the highest score(s) will be declared the winners along with the developers of the winning routines.

Please do not crack your own submission.

Best of luck. :)

Leaderboard

Last Updated Sept 2, 10:45 AM EST

Immovable Barriers (non-cracked submissions):

  1. CJam, 105 [Dennis]

Unstoppable Forces (crackers):

  1. Dennis [Java, 269; C, 58; Mathematica, 29]
  2. Martin Büttner [Java, 245]

COTO

Posted 2014-08-31T08:50:44.447

Reputation: 3 701

11May I suggest [cops-and-robbers] as the tag for these challenges? I think it's a fairly established name for such games in security and it will probably provoke more interest then [adversarial]. – Martin Ender – 2014-08-31T09:01:29.083

Sure. I'll change it now. – COTO – 2014-08-31T09:02:30.250

What kind of output is acceptable? STDOUT, return, etc... – Ypnypn – 2014-08-31T21:01:58.900

Both stdout and a simple return are acceptable. – COTO – 2014-08-31T21:12:25.503

2Your example is incorrect; its signature is 9. %5 can return anything from -4 to 4, inclusive. – Keith Randall – 2014-09-01T05:07:35.190

I forgot about that. I'll correct the example. Thanks for the heads up. – COTO – 2014-09-01T06:55:34.433

I've made an attempt to crack a key, but the answer was edited since the original post was invalid. Does that mean I get to try again?

– Dennis – 2014-09-01T21:16:06.517

1@Dennis I would be fine with you trying again. It was my fault that it was messed up. – Stretch Maniac – 2014-09-01T22:25:17.090

@COTO Your example still doesn't have a key of 5. -2^31 is an edge case, since abs() doesn't work on it.

– Geobits – 2014-09-02T13:47:09.260

burbles lips OK. Third time's the charm. – COTO – 2014-09-02T13:50:25.057

Answers

7

CJam, 105 bytes

1q~]4G#b2A#md"M-k^XM-WHM-n^GM-0%M-uwM-gM-^XeM-kM-^VO^Ph,M-^MM-^PM-qM-!M-8M-AM-OM-~tM-^FM-cM-h^AM-0M-0M-lM-@M-^[MF=M-^Z^SM-1M-KM-T2M-9M-UmSM-N
M-8M-^^M-n$4M-^M^SM-x M-OM-^@^?"256b@D#Y256#%2+md!A3#*)%)%

The above uses caret and M notation, since it contains unprintable character. After converting the byte stream into an integer (256b), the following code gets executed:

1q~]4G#b2A#md
12313030820310059479144347891900383683119627474072658988524821209446180284434934346766561958060381533656780340359503554566598728599799248566073353154035839
@D#Y256#%2+md!A3#*)%)%

You can try this version online in the CJam interpreter.

How it works

This submission uses number theory instead of obfuscation. The program will return 0 for almost all inputs. From the few inputs that result in a non-zero output, a secret modulus is derived that is applied to the 10 least significant bits of the third integer.

The most efficient way of solving this challenge (that I can think of) would be factorizing the 512 bit integer, which I hope won't be achievable in 72 hours.

" Prepend 1 to the numbers read from STDIN and convert the resulting array into an integer
  (“N”) by considering them digits of a base 2**32 number.                                 ";

1q~]4G#

" Compute “N / 1024” and “N % 1024”.                                                       ";

2A#md

" Push a carefully selected 512 bit semi-prime (“S”).                                      ";

12313030820310059479144347891900383683119627474072658988524821209446180284434934346766561958060381533656780340359503554566598728599799248566073353154035839

" Compute P = (N / 1024) ** 13 % 2 ** 256 + 2.                                             ";

@D#Y256#%2+

" Compute “S / P” and “S % P”.                                                             ";

md

" Compute “M = (S / P) % (1000 * !(S % P) + 1) + 1”.

  “M” is the key if P is a divisor of S; otherwise, “M == 1”.                              ";

!A3#*)%)

" Compute the final output: “N % 1024 % M”.                                                ";

%

Example run

$ base64 -d > outputs.cjam <<< MXF+XTRHI2IyQSNtZCLrGNdI7gewJfV355hl65ZPEGgsjZDxobjBz/50huPoAbCw7MCbTUY9mhOxy9QyudVtU84KuJ7uJDSNE/ggz4B/IjI1NmJARCNZMjU2IyUyK21kIUEzIyopJSkl
$ wc -c outputs.cjam
105 outputs.cjam
$ LANG=en_US cjam outputs.cjam < outputs.secret; echo
1
$ LANG=en_US cjam outputs.cjam <<< '1 2 3'; echo
0

Dennis

Posted 2014-08-31T08:50:44.447

Reputation: 196 637

You're just too darn good with the encryption stuff. ;) – COTO – 2014-09-01T06:54:10.527

11"This submission uses number theory instead of obfuscation." Looks at code "Hmm, right." – ɐɔıʇǝɥʇuʎs – 2014-09-01T16:34:21.850

4

Java - 269

Thanks for everyone's patience, this should now be fixed.

shortened:

int a(int a,int b,int c){double d=180-360.0/(int)(Math.abs(Math.sin(a*60))*50+2),e=180-360.0/(int)(Math.abs(Math.cos(b*60))*50+2),f=180-360.0/(int)(Math.atan2(c*60, a*60)*51+2);if(Math.abs(d+e+f-360)<.1)return Integer.valueOf((int)d+""+(int)e+""+(int)f);else return 1;}

Not shortened:

int a(int a, int b, int c) {
    double d = 180 - 360.0 / (int) (Math.abs(Math.sin(a * 60)) * 50 + 2);
    double e = 180 - 360.0 / (int) (Math.abs(Math.cos(b * 60)) * 50 + 2);
    double f = 180 - 360.0 / (int) (Math.atan2(c * 60, a * 60) * 51 + 2);
    if (Math.abs(d + e + f - 360) < .1)
        return Integer.valueOf((int) d + "" + (int) e + "" + (int) f);
    else
        return 1;
}

Stretch Maniac

Posted 2014-08-31T08:50:44.447

Reputation: 3 971

You can save four characters by changing double e,f,d=...;e=...;f=...; to double d=...,e=...,f=...; – Ypnypn – 2014-09-01T00:17:30.650

@Ypnypn Thanks! added to golfed version. – Stretch Maniac – 2014-09-01T00:28:26.877

Here goes nothing: 98 – Dennis – 2014-09-01T06:03:30.957

Just from bruteforcing... 110? – Vectorized – 2014-09-01T06:08:41.270

@MartinBüttner: Thanks, fixed. – Stretch Maniac – 2014-09-01T15:34:55.433

@StretchManiac For some negative inputs, I will get a java.lang.NumberFormatException. Anyway, during my bruteforce, I used this formula instead for a bit of optimization: ((int)d*1000000+(int)e)*1000+(int)f. – Vectorized – 2014-09-01T15:47:52.790

I don't think this will work, because you'll get - inside your number during concatenation (I believe that's what bitpwner is talking about). I think you can golf this down though, by not casting 360 to double but by using 360.0 instead (or maybe 360., not sure if that works in Java). – Martin Ender – 2014-09-01T15:50:10.740

So, @bitpwner and I both guessed wrong? – Dennis – 2014-09-01T16:30:15.533

@StretchManiac as long as your answer has not been cracked, you should probably just fix it (with an edit). – Martin Ender – 2014-09-01T16:46:18.783

I get 121 outputs, but I'm not entirely sure of it. – Martin Ender – 2014-09-01T17:43:38.803

@MartinBüttner that is incorrect. – Stretch Maniac – 2014-09-01T18:20:52.230

1

Second attempt (with explicit permission): 122

– Dennis – 2014-09-01T23:18:11.980

1@Dennis nice job! (That's it) – Stretch Maniac – 2014-09-01T23:46:12.997

If it wasn't for @MartinBüttner, I would have answered 121 as well. Then I found out that, on very rare occasions, Math.abs(Math.sin(a*60)) == 1.0. The first positive a that satisfies the condition is 152,073,634... -- +1 for making me learn Java. – Dennis – 2014-09-01T23:52:39.750

@Dennis Not bad! Did you just brute force that? Odd that this doesn't happen for cos as well then. – Martin Ender – 2014-09-02T07:38:04.547

@MartinBüttner: Math.cos(0) == 1.0. I initially guessed the possible values of the trigonometric functions. Since I got 121 as the key, I ran a brute-force search to check if the sine could return 1 as well. – Dennis – 2014-09-02T14:27:09.683

@Dennis Whoops, totally forgot about that... odd that I still got 121 without considering cos return 1.0 then. – Martin Ender – 2014-09-02T14:31:34.240

Okay something's odd. Allowing for cos == 1.0 I get 122. also allowing for sin == 1.0 I get 123. – Martin Ender – 2014-09-02T14:33:46.767

@MartinBüttner: I get 51 possible values for d and e, 160 for f. These are the 122 solutions they generate: http://pastebin.com/dX5ieFDw

– Dennis – 2014-09-02T14:56:51.830

1@Dennis In that case, you're forgetting 1 and your answer is incorrect as well ;) (123 being correct... someone come along and grab the cracking score...). And I guess Stretch Maniac didn't account for sin == 1.0 when he said that 122 is correct. – Martin Ender – 2014-09-02T15:00:53.200

Let us continue this discussion in chat.

– Dennis – 2014-09-02T15:03:52.060

Sorry about me getting the wrong answer. To make up for it I made an explanation of the right answer here. I actually really enjoyed making this :)

– Stretch Maniac – 2014-09-02T22:11:33.347

2

A Sampler

Not an official entry of course, and the character count is too high, but I figure if anyone wants a mind-numbing challenge, they can try to determine how many unique outputs the following function produces (given three inputs as described in the OP):

function z(y,_,$){M=[];N=[];O=[];C=1792814437;P=72;r=t=0;(f=function(a,k,L){if(k<a.length){for(L=a[k],a[k]=0;a[k]<L;a[k]++)f(a,k+1)}else
if(!t){f([P-1,P-1],0,++t);N=M;while(t<2*P){s=!(t&1);f([P,P,P,P],0,++t);r=r||(s?0:t);t&1&&(N=O);O=[]}}else
((t<2)&&(((d=P*a[0]+(P+1)*a[1]+P)<(P<<6))&&(M[d]=(((y^~_)>>a[0])+((_^~$)>>(a[0]-32)))&1),((a[1]<P-a[0])&&
(M[a[1]+(P+1)*a[0]]=(($^C)>>a[0]+16-a[1])&1))||1))||((t&1)&&((O[P*a[2]+a[3]]|=M[a[1]+P*a[2]]&N[P*a[0]+a[3]]&&
!(a[0]-a[1]))||1))||(s|=N[(a[0]+1)*a[1]+a[3]]);})([],0,0);return r;}

In fact, I'm so confident that it's uncrackable, I'll award anyone who cracks it the "Supreme Unstoppable Force of Nature Award".

Because really, they deserve it.

COTO

Posted 2014-08-31T08:50:44.447

Reputation: 3 701

1You should put up a bounty for it! – Orby – 2014-09-01T03:37:28.510

1@Orby That would be nice, but it's hard to award a bounty to a comment. – Geobits – 2014-09-01T04:29:20.427

I added some indentation to this. – Martin Ender – 2014-09-01T18:05:20.027

@COTO is this challenge still on? – Soham Chowdhury – 2014-09-15T18:55:12.537

@SohamChowdhury: Absolutely. If you figure it out, I'll expound your victory in the OP. If not, let me know and I'll post the solution. – COTO – 2014-09-15T22:08:05.487

2

Java - 245

Cracked by Martin Büttner

int a(int[]_$){return $($($_(_$[0],0)))+$_(_$[1],1)*11+$($($_(_$[1+1],0)))*(1+1);}int $_(int $,int $_){int OO=0,o=1,O=1;for($=$<0?-$:$;++O*O<=$;OO+=$%O<1?O:0,o+=$%O<1?1:0,$/=$%O<1?O--:1);return $_>0?o:OO+$;}int $(int $){return(int)Math.sqrt($);}

Feed the input as an int array: a(new int[]{1,2,3}). I'm not expecting it to go 72 hours, but have fun with it.

Here it is with line breaks, to make it a bit more readable:

int a(int[]_$){return $($($_(_$[0],0)))+$_(_$[1],
1)*11+$($($_(_$[1+1],0)))*(1+1);}int $_(int $,int
$_){int OO=0,o=1,O=1;for($=$<0?-$:$;++O*O<=$;OO+=
$%O<1?O:0,o+=$%O<1?1:0,$/=$%O<1?O--:1);return $_>
0?o:OO+$;}int $(int $){return(int)Math.sqrt($);}

Geobits

Posted 2014-08-31T08:50:44.447

Reputation: 19 061

Just from bruteforcing... 90? – Vectorized – 2014-09-01T06:23:17.590

@bitpwner Nope, sorry. – Geobits – 2014-09-01T06:27:11.930

1

I deobfuscated it a bit: http://pastebin.com/8pvvfFYB (I hope I didn't make any mistakes while replacing variable names.)

– Martin Ender – 2014-09-01T10:44:33.607

4Okay, here is my attempt: 965? – Martin Ender – 2014-09-01T11:35:16.703

1@MartinBüttner Correct. Thanks for the obfuscated version :D – Geobits – 2014-09-01T15:18:41.767

2

C, 58 bytes (Cracked)

A simple one:

f(a,b,c){return(long long)a*b*c-0x1d21344f8479d61dLL?0:a;}

Orby

Posted 2014-08-31T08:50:44.447

Reputation: 844

27 (-15485867, -1299721, -104287, 0, 104287, 1299721, 15485867). – Dennis – 2014-09-01T05:55:27.623

That was fast :) – Orby – 2014-09-01T05:56:42.017

1

Mathematica, 29 bytes, Key: 715, Cracked by Dennis

This is just a fixed version of my initial answer, which didn't work for non-positive inputs.

f=Plus@@Mod[NextPrime@#,240]&

Takes a list of integers like

f[{1,2,3}]

Martin Ender

Posted 2014-08-31T08:50:44.447

Reputation: 184 808

I found 349 unique outputs. The range was from 3 to 717. – PhiNotPi – 2014-09-01T11:57:20.980

@PhiNotPi Wrong. (I double-checked) – Martin Ender – 2014-09-01T12:00:19.067

Well, I found my mistake and the right answer. Too late though. – PhiNotPi – 2014-09-01T12:25:37.823

1If the stuff I pieced together from the Mathematica documentation and WolframAlpha is correct, the key is 715 (3 ... 717). – Dennis – 2014-09-01T16:18:01.583

@Dennis Correct. – Martin Ender – 2014-09-01T16:19:09.330

2Mathematica looks like a nice language, but it's either too darn expensive or I'm too darn cheap... – Dennis – 2014-09-01T16:23:10.490

@Dennis Yay for student licenses! Let's hope that there will be some decent open source implementations of Wolfram Language in the not so distant future. (Actually, you could just buy a Raspberry Pi, that's probably cheaper.) – Martin Ender – 2014-09-01T16:24:18.210

0

207 characters, in C/C++, not yet obfuscated:

int x(int a, int b, int c) {
    int d, e, f;
    for (int i=0; i!=1<<31; ++i) {
        d=10*(b-a);
        e=a*(28-c)-b;
        f=a*b-2.7*c;
        a += d;
        b += e;
        c += f;
    }
    return ((a%5+5)*10+(b%5+5))*10+c%5+5;
}

ldgabbay

Posted 2014-08-31T08:50:44.447

Reputation: 109

Just trying my luck... 729. – Vectorized – 2014-09-01T08:14:21.627

@bitpwner Damn, I was just gonna say that. :D ... If that's wrong, that's the upper bound though. – Martin Ender – 2014-09-01T08:15:05.160

2This is not a valid submission. All assignments inside the loop may result in signed integer overflow, which has undefined behavior. – Dennis – 2014-09-01T23:26:31.730