![]() |
|
|
|
#1
|
|||
|
|||
|
The correct key will decrypt some fixed ciphertext into fixed plaintext. The known plaintext is apparently the digits of pi as hex digits.
Code:
#include <iostream>
#include <sstream>
#include <stdint.h>
int main(int argc, char* argv[])
{
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " <key>" << std::endl;
return 0;
}
uint64_t k;
std::stringstream ss;
ss << argv[1];
ss >> std::hex >> k;
if (!ss) {
std::cout << "Invalid key!" << std::endl;
return 0;
}
uint32_t s = 0;
for (int i = 0; i < 10000; ++i) {
s = s * 1664525 + 1013904223;
int j = s % 64;
uint64_t a = (k >> j) & 1;
uint64_t b = (k & 1) << j;
k ^= a ^ b;
}
uint64_t r = k ^ 0x1221777f1c3e4341;
if (r == 0x3141592653589793) {
std::cout << "Correct key!" << std::endl;
} else {
std::cout << "Wrong key!" << std::endl;
}
return 0;
}
|
|
#2
|
|||
|
|||
|
The keys are 0x45584554CF4F4C52 and 0x455845544F4F4C53.
SPOILERS AHEAD The key observation is that each round has the following transformation pattern: Code:
0...0 -> 0...0 k_n -> k' (with k_n being the key for round n and k' := a ^ b in the original algorithm)
0...0 -> 0...0 k_n -> k_{n+1} (with k_{n+1} := k_n ^ k')
1...0 -> 0...1
1...0 -> 1...1
0...1 -> 1...0
0...1 -> 1...1
1...1 -> 1...1
1...1 -> 0...0
Some special care needs to be taken for rounds with zero shifts as these are effectively NOPs. |
|
#3
|
|||
|
|||
|
Could you give a sample of the ciphertext encrypted?
|
|
#4
|
|||
|
|||
|
The ciphertext here is 0x3141592653589793 ^ 0x1221777f1c3e4341.
Forward step is k[i][j] = k[i][0] = k[i-1][j] ^ k[i-1][0], where j = gamma[i]. Simplify and you will see the bit 0 on the current step is result of comparison of bits on the previous step. Reverse step was explained by @ArC: build the bitlogic operation table and use it to restore source bits. You need to know 2 steps to restore the bit value. |
|
#5
|
|||
|
|||
|
Congrats ArC for getting the key(s).
I forgot to post my solution program here before: Code:
#include <iostream>
#include <sstream>
#include <stdint.h>
#include <vector>
#include <set>
int main(int argc, char* argv[])
{
/* identity matrix */
int bits[64][64] = {0};
for (int i = 0; i < 64; ++i) {
bits[i][i] = 1;
}
/* build the parity check matrix */
uint32_t s = 0;
for (int i = 0; i < 10000; ++i) {
s = s * 1664525 + 1013904223;
int j = s % 64;
for (int i = 0; i < 64; ++i) {
int t = bits[0][i];
bits[0][i] ^= bits[j][i];
bits[j][i] ^= t;
}
}
/* right-hand side "known" vector */
uint64_t r = 0x3141592653589793 ^ 0x1221777f1c3e4341;
int rv[64] = {0};
for (int i = 0; i < 64; ++i) {
rv[i] = (r >> i) & 1;
}
/* initialize row permutation lookup table */
int p[64] = {0};
for (int i = 0; i < 64; ++i) {
p[i] = i;
}
/* run gaussian elimination */
std::set<int> bf;
std::set<int> pbf;
for (int i = 0; i < 64; ++i) {
bool ok = false;
for (int j = i; j < 64; ++j) {
if (bf.find(p[j]) != bf.end()) {
continue;
}
if (bits[p[j]][i]) {
int t = p[i];
p[i] = p[j];
p[j] = t;
ok = true;
break;
}
}
if (!ok) {
/* system is singular */
/* reduce it from an NxN system to a (N-1)x(N-1) system */
pbf.insert(p[i]); /* brute force this bit later */
bf.insert(i);
continue;
}
for (int j = i+1; j < 64; ++j) {
if (pbf.find(p[j]) != pbf.end()) {
continue;
}
if (bits[p[j]][i]) {
for (int k = 0; k < 64; ++k) {
bits[p[j]][k] ^= bits[p[i]][k];
}
rv[p[j]] ^= rv[p[i]];
}
}
}
std::vector<int> bfv;
bfv.insert(bfv.end(), bf.begin(), bf.end());
/* back-substitute to solve */
std::set<int> solved;
int bf_max = 1 << bf.size();
for (int bfc = 0; bfc < bf_max; ++bfc) {
int v[64] = {0};
for (size_t i = 0; i < bfv.size(); ++i) {
v[bfv[i]] = (bfc >> i) & 1; /* insert the bits we are brute forcing */
solved.insert(bfv[i]);
}
for (int i = 0; i < 64; ++i) {
int j = 64 - i - 1;
if (bf.find(j) != bf.end()) {
continue; /* we are guessing this */
}
int s = -1;
int t = rv[p[j]];
for (int k = 0; k < 64; ++k) {
if (bits[p[j]][k]) {
if (solved.find(k) != solved.end()) {
t ^= v[k];
} else {
s = k;
}
}
}
if (s != -1) {
/* solved one bit */
v[s] = t;
solved.insert(s);
}
}
/* copy the solution to an int */
uint64_t sol = 0;
for (int i = 0; i < 64; ++i) {
sol |= (uint64_t(v[i]) << i);
}
/* run the original algorithm */
uint64_t key = sol;
uint32_t s = 0;
for (int i = 0; i < 10000; ++i) {
s = s * 1664525 + 1013904223;
int j = s % 64;
uint64_t a = (key >> j) & 1;
uint64_t b = (key & 1) << j;
key ^= a ^ b;
}
/* test the soluton */
r = key ^ 0x1221777f1c3e4341;
if (r == 0x3141592653589793) {
std::cout << std::hex << sol << std::endl;
}
/* repeat to find all keys */
solved.clear();
}
return 0;
}
|
| The Following User Says Thank You to dila For This Useful Post: | ||
Kjacky (10-09-2016) | ||
![]() |
| Tags |
| c++, crackme |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| armadillo extraction error ES1 demystified | bollygud | General Discussion | 9 | 02-27-2005 20:42 |