Merge pull request #269 from xexyl/2019-diels-grabsch2
Fixes in 2019/diels-grabsch2
This commit is contained in:
2 files changed
+50
-45
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
# Most self-aware
|
# Most self-aware
|
||||||
|
|
||||||
Volker Diels-Grabsch
|
Volker Diels-Grabsch
|
||||||
<https://njh.eu>
|
<https://njh.eu>
|
||||||
|
|
||||||
## To build:
|
## To build:
|
||||||
|
|
||||||
@@ -9,6 +9,12 @@
|
|||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[Cody Boone Ferguson](/winners.html#Cody_Boone_Ferguson) made the author's
|
||||||
|
statement that the entry compiles cleanly true by fixing `warning: a function
|
||||||
|
declaration without a prototype is deprecated in all versions of C` (in
|
||||||
|
main()). Not strictly necessary but if he's making fixes he might as well. Thank
|
||||||
|
you Cody!
|
||||||
|
|
||||||
## To run:
|
## To run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -30,65 +36,65 @@ that you are dreaming? Or is it, wake up from the simulation?
|
|||||||
|
|
||||||
## Author's comments:
|
## Author's comments:
|
||||||
|
|
||||||
### Almost a hash collision
|
### Almost a hash collision:
|
||||||
|
|
||||||
This program prints its own SHA-512 hash. To verify, run:
|
This program prints its own SHA-512 hash. To verify, run:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sha512sum prog.c; ./prog
|
$ sha512sum prog.c; ./prog
|
||||||
|
43bd0c4381a5078d2850fca9ae5e0647596bcc03cd67d8d973ad02ff35c316c728e7b347ca70abe6c74e745e63646cc7643cb0cffcd3d9a969cbf31a7ce5bf68 prog.c
|
||||||
|
43bd0c4381a5078d2850fca9ae5e0647596bcc03cd67d8d973ad02ff35c316c728e7b347ca70abe6c74e745e63646cc7643cb0cffcd3d9a969cbf31a7ce5bf68
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
It started as a complicated "hello world" program that prints some random hex
|
||||||
|
digits instead of "hello world". Then, the digits were adjusted until the
|
||||||
|
resulting program matched its own SHA-512 hash, via brute force through all
|
||||||
|
2^512 possibilities. Just kidding.
|
||||||
|
|
||||||
43bd0c4381a5078d2850fca9ae5e0647596bcc03cd67d8d973ad02ff35c316c728e7b347ca70abe6c74e745e63646cc7643cb0cffcd3d9a969cbf31a7ce5bf68 prog.c
|
This wasn't brute force, but involved quite some math, using a variant of Jane
|
||||||
43bd0c4381a5078d2850fca9ae5e0647596bcc03cd67d8d973ad02ff35c316c728e7b347ca70abe6c74e745e63646cc7643cb0cffcd3d9a969cbf31a7ce5bf68
|
Doe's algorithm for generating SHA-512 collisions efficiently. If you still
|
||||||
|
don't believe me, you are right.
|
||||||
It started as a complicated "hello world" program that prints some
|
|
||||||
random hex digits instead of "hello world". Then, the digits were
|
|
||||||
adjusted until the resulting program matched its own SHA-512 hash,
|
|
||||||
via brute force through all 2^512 possibilities. Just kidding.
|
|
||||||
|
|
||||||
This wasn't brute force, but involved quite some math, using a variant
|
|
||||||
of Jane Doe's algorithm for generating SHA-512 collisions efficiently.
|
|
||||||
If you still don't believe me, you are right.
|
|
||||||
|
|
||||||
No crypto was broken in the making of this program!
|
No crypto was broken in the making of this program!
|
||||||
|
|
||||||
The trick is that under the hood, this program is a quine! That is,
|
The trick is that under the hood, this program is a quine! That is, it is aware
|
||||||
it is aware of its own source code in string representation. But
|
of its own source code in string representation. But instead of printing that
|
||||||
instead of printing that string, its SHA-512 hash is computed and
|
string, its SHA-512 hash is computed and printed. So two major steps were
|
||||||
printed. So two major steps were needed to obfuscate this program:
|
needed to obfuscate this program:
|
||||||
|
|
||||||
- Step 1: Hide the quine
|
1. Hide the quine
|
||||||
- Step 2: Hide the SHA-512 computation
|
2. Hide the SHA-512 computation
|
||||||
|
|
||||||
Note that both steps were achieved mostly algorithmically. The code
|
Note that both steps were achieved mostly algorithmically. The code
|
||||||
is otherwise as short and as clean as possible. The implementation is
|
is otherwise as short and as clean as possible. The implementation is
|
||||||
portable C99 code for 32-bit and 64-bit systems that compiles without
|
portable C99 code for 32-bit and 64-bit systems that compiles without
|
||||||
any warnings on current GCC and Clang versions even with -Wextra and
|
any warnings on current GCC and Clang versions even with `-Wextra
|
||||||
-Weverything. The program is composed of mostly pure functions, and
|
-Weverything`. The program is composed of mostly pure functions, and
|
||||||
the "#define"s are only used to shorten redundant parts of the code,
|
the `#define`s are only used to shorten redundant parts of the code,
|
||||||
they are not meant to obfuscate much. Having that out of the way, how
|
they are not meant to obfuscate much. Having that out of the way, how
|
||||||
are step 1 and step 2 achieved then?
|
are step 1 and step 2 achieved then?
|
||||||
|
|
||||||
Regarding step 1, note that the large string literal "`x{bh}ndbq..."
|
Regarding step 1, note that the large string literal
|
||||||
|
|
||||||
|
"`x{bh}ndbq..."
|
||||||
|
|
||||||
in the last line doesn't look like C code. Moreover, it is too short
|
in the last line doesn't look like C code. Moreover, it is too short
|
||||||
to contain even a compressed representation of all preceding and
|
to contain even a compressed representation of all preceding and
|
||||||
subsequent code. Instead, the preceding code including the '"'
|
subsequent code. Instead, the preceding code including the `"`
|
||||||
character has already been run through the SHA-512 computation by an
|
character has already been run through the SHA-512 computation by an
|
||||||
external script. The internal state of the SHA-512 computation at
|
external script. The internal state of the SHA-512 computation at
|
||||||
that point has been extracted and encoded into the string. This works
|
that point has been extracted and encoded into the string. This works
|
||||||
because the preceding code is exactly 1280 bytes, which is a multiple
|
because the preceding code is exactly 1280 bytes, which is a multiple
|
||||||
of 1024 bits, the block size of SHA-512. The program itself then just
|
of 1024 bits, the block size of SHA-512. The program itself then just
|
||||||
continues the SHA-512 computation from that point, adding the last
|
continues the SHA-512 computation from that point, adding the last
|
||||||
1024-bit block, finalizing the hash and printing it. The last block
|
1024-bit block, finalizing the hash and printing it. The last block
|
||||||
is taken directly from the string literal at runtime, plus the
|
is taken directly from the string literal at runtime, plus the
|
||||||
subsequent code which consists of just '"', ';' and '\n'. Those 3
|
subsequent code which consists of just `"`, `;` and `\n`. Those 3
|
||||||
characters are short enough to be hidden in ... well, let's leave this
|
characters are short enough to be hidden in ... well, let's leave this
|
||||||
as an exercise for the reader.
|
as an exercise for the reader.
|
||||||
|
|
||||||
To summarize, the main trick here is to move the string constant to
|
To summarize, the main trick here is to move the string constant to
|
||||||
the end, so the preceding code can be precalculated and the subsequent
|
the end, so the preceding code can be pre-calculated and the subsequent
|
||||||
code is short enough to be hidden anywhere.
|
code is short enough to be hidden anywhere.
|
||||||
|
|
||||||
Regarding step 2, almost all macros, functions and variables have
|
Regarding step 2, almost all macros, functions and variables have
|
||||||
@@ -96,12 +102,11 @@ meaningless single-letter names. Moreover, the SHA-512 computation is
|
|||||||
reduced to the special case of adding just one block and immediately
|
reduced to the special case of adding just one block and immediately
|
||||||
finalizing it, where the total size is known in advance.
|
finalizing it, where the total size is known in advance.
|
||||||
|
|
||||||
But this is not enough! For SHA-512 we need 8 initial hash values and
|
But this is not enough! For SHA-512 we need 8 initial hash values and 80 round
|
||||||
80 round constants, which are very characteristic. If any of them
|
constants, which are very characteristic. If any of them appeared in the
|
||||||
appeared in the source, a quick web search (e.g. for 0x428a2f98d728ae22)
|
source, a quick web search (e.g. for `0x428a2f98d728ae22`) would tell the reader
|
||||||
would tell the reader immediately what's going on. Because of the
|
immediately what's going on. Because of the pre-computation, the 8 initial hash
|
||||||
precomputation, the 8 initial hash values are not needed, but the
|
values are not needed, but the 80 round constants are.
|
||||||
80 round constants are.
|
|
||||||
|
|
||||||
It is almost impossible to hide those 640 bytes in the source.
|
It is almost impossible to hide those 640 bytes in the source.
|
||||||
Luckily, the round constants where not chosen by fair dice rolls.
|
Luckily, the round constants where not chosen by fair dice rolls.
|
||||||
@@ -117,12 +122,12 @@ would have been 32-bit values. However, SHA-512 needs the cube roots
|
|||||||
at 64-bit precision, while double has only 53-bit mantissa precision.
|
at 64-bit precision, while double has only 53-bit mantissa precision.
|
||||||
|
|
||||||
Using higher-precision floating point is no option here, because the
|
Using higher-precision floating point is no option here, because the
|
||||||
program is meant to be portable C99. And implementing
|
program is meant to be portable C99. And implementing arbitrary-precision
|
||||||
arbitrary-precision arithmetic would have been overkill and would have
|
arithmetic would have been overkill and would have increased the program size
|
||||||
increased the program size dramatically.
|
dramatically.
|
||||||
|
|
||||||
Instead, the program just multiplies. That is, it computes cubes
|
Instead, the program just multiplies. That is, it computes cubes
|
||||||
instead of cube roots. It steps through cube root candidates via
|
instead of cube roots. It steps through cube root candidates via
|
||||||
bisection, and calculates their cubes until those match the given
|
bisection, and calculates their cubes until those match the given
|
||||||
prime. Thus, we don't need floating point and can use 64-bit integers
|
prime. Thus, we don't need floating point and can use 64-bit integers
|
||||||
instead. But that still doesn't solve the problem as it poses the
|
instead. But that still doesn't solve the problem as it poses the
|
||||||
@@ -144,18 +149,18 @@ around 7, so we need to compute with 3 bits for the integral part and
|
|||||||
64 bits for the fractional part.
|
64 bits for the fractional part.
|
||||||
|
|
||||||
The reason for challenge 3 is that the cube of a 67-bit number is
|
The reason for challenge 3 is that the cube of a 67-bit number is
|
||||||
3*67=201 bits, but fortunately we only need to check if it is close
|
`3*67=201` bits, but fortunately we only need to check if it is close
|
||||||
enough to the actual prime. It won't be exactly e.g. "409.0000..."
|
enough to the actual prime. It won't be exactly e.g. `409.0000...`
|
||||||
anyway, but must be accurate enough to distinguish the correct cube
|
anyway, but must be accurate enough to distinguish the correct cube
|
||||||
root from its neighbours, e.g. the same number with the 64th
|
root from its neighbours, e.g. the same number with the 64th
|
||||||
fractional digit increased.
|
fractional digit increased.
|
||||||
|
|
||||||
The solution here is to split the 67-bit input into 3 integers of
|
The solution here is to split the 67-bit input into 3 integers of
|
||||||
24-bit each. Those are cubed using the multiplication formulas
|
24-bit each. Those are cubed using the multiplication formulas
|
||||||
numbers with 3 "digits" of base 2^24 each. During that calculation,
|
numbers with 3 "digits" of base `2^24` each. During that calculation,
|
||||||
at most two values are multiplied at once, and the accuracy of
|
at most two values are multiplied at once, and the accuracy of
|
||||||
intermediate values is reduced using right-shifts as far as allowable
|
intermediate values is reduced using right-shifts as far as allowable
|
||||||
for the computation. Finding woking right-shift values was mostly
|
for the computation. Finding working right-shift values was mostly
|
||||||
trial-and-error, producing correct results for the first 80 primes,
|
trial-and-error, producing correct results for the first 80 primes,
|
||||||
but not much more. Moreover, for some terms it was possible to leave
|
but not much more. Moreover, for some terms it was possible to leave
|
||||||
them out entirely, especially higher-order terms of the last digits.
|
them out entirely, especially higher-order terms of the last digits.
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ b*c>>32))*a>>21)+(3*a*a*b>>6)+((b>>4)*(b>>4)*b>>46))>>18)+a*a*a)h(m,t((b<<16)|(c
|
|||||||
^(~a&c))i(r,b(a,1)^b(a,8)^(a>>7))g(o,0,0,c<8;c++,a*256+w[b*8+c])g(d,0,0,c<13;c++,a*31+w[b*13+c]-96)g(p,0,4,c;c/=2,a|c*m(b,a|c,a)
|
^(~a&c))i(r,b(a,1)^b(a,8)^(a>>7))g(o,0,0,c<8;c++,a*256+w[b*8+c])g(d,0,0,c<13;c++,a*31+w[b*13+c]-96)g(p,0,4,c;c/=2,a|c*m(b,a|c,a)
|
||||||
)g(q,0,1ull<<63,c;c/=2,a|c*m(b,p(b),a|c))g(v,b>1,2,c<b;c++,a&&b%c)g(l,b?l(b-1)+1:2,a,!v(c);c++,c+1)j(n,z d=a[7]+u(a[4])+s(a[4],a
|
)g(q,0,1ull<<63,c;c/=2,a|c*m(b,p(b),a|c))g(v,b>1,2,c<b;c++,a&&b%c)g(l,b?l(b-1)+1:2,a,!v(c);c++,c+1)j(n,z d=a[7]+u(a[4])+s(a[4],a
|
||||||
[5],a[6])+q(l(*b))+c[*b%16];f(8,a[7-e]=e-3?e-7?a[6-e]:d+_a(a[0])+x(a[1],a[2],a[3]):d+a[3])f(16*(*b%16>14),c[e]+=c[(e+9)%16]+r(c[
|
[5],a[6])+q(l(*b))+c[*b%16];f(8,a[7-e]=e-3?e-7?a[6-e]:d+_a(a[0])+x(a[1],a[2],a[3]):d+a[3])f(16*(*b%16>14),c[e]+=c[(e+9)%16]+r(c[
|
||||||
(e+1)%16])+_(c[(e+14)%16])))j(k,f(8,b[e]=a[e])f(80,n(a,&e,c))f(8,a[e]+=b[e]))int main(){z a[8],b[8],c[16];f(8,a[e]=d(e))f(16,c[e
|
(e+1)%16])+_(c[(e+14)%16])))j(k,f(8,b[e]=a[e])f(80,n(a,&e,c))f(8,a[e]+=b[e]))int main(void){z a[8],b[8],c[16];f(8,a[e]=d(e))f(16,c[e
|
||||||
]=e-15?o(e):d(8))k(a,b,c);f(16,c[e]=e?e-15?0:11264:1ull<<63)k(a,b,c);f(8,printf("%016llx%s",a[e],e-7?"":"\n"))return!w;}y*w=(y*)
|
]=e-15?o(e):d(8))k(a,b,c);f(16,c[e]=e?e-15?0:11264:1ull<<63)k(a,b,c);f(8,printf("%016llx%s",a[e],e-7?"":"\n"))return!w;}y*w=(y*)
|
||||||
"crsmyiajqhwy{unwa|hjoi`hlxhpxrzb~edko~rtr~ileqyjk`znqgsuitvgqnfdfa||wedvnmhozkpokootqzcexeld~oibqzpcsuw{ib{x`m`hsa`jmn}wcfzpb";
|
"crsmyiajqhwy{unwa|hjoi`hlxhpxrzb~edko~rtr~ileqyjk`znqgsuitvgqnfdfa||wedvnmhozkpokootqzcexeld~oibqzpcsuw{ib{x`m`hsa`jmn}wcfzpb";
|
||||||
Reference in new issue
Block a user