Last active
April 3, 2024 18:05
-
-
Save FelixMartel/419ff299dbfae388692c5c9c4a335f5a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//-O2 -std=c++17 -lpthread -lcrypto | |
// 4 GB - 1 hour | |
#include <openssl/des.h> | |
#include <iostream> | |
#include <vector> | |
#include <cstring> | |
#include <thread> | |
constexpr uint64_t flip_endian(uint64_t x) { | |
return __builtin_bswap64(x); | |
} | |
uint64_t start = flip_endian(0x7765616B68617368); | |
uint64_t end = flip_endian(0xda99d1ea64144f3e); | |
#define N_TARGET (1ul << 32) | |
#define N_TOT (N_TARGET << 2) | |
#define N_BYTE (N_TOT >> 2) | |
#define MAX_DEPTH 30 | |
#define MASK_FULL (N_TOT - 1) | |
#define MASK_2 0x3 | |
std::vector<uint8_t> targets(N_BYTE,0); | |
std::vector<uint64_t> key_set(3); | |
// Usage: | |
// SpeedMeasurer speed; | |
// // ... | |
// speed.measure(items_processed); | |
// // ... | |
// cout << speed; | |
struct SpeedMeasurer { | |
std::chrono::high_resolution_clock::time_point start; | |
uint64_t items; | |
SpeedMeasurer() : start{std::chrono::high_resolution_clock::now()}, items{0} {} | |
void measure(uint64_t items_processed) { items += items_processed; } | |
double items_per_second() const { | |
const auto stop = std::chrono::high_resolution_clock::now(); | |
using seconds_t = std::chrono::duration<double, std::chrono::seconds::period>; | |
const seconds_t seconds = stop - start; | |
return static_cast<double>(items) / seconds.count(); | |
} | |
}; | |
std::ostream& operator<<(std::ostream& os, const SpeedMeasurer& speed) { | |
os << speed.items_per_second() << "/s"; | |
return os; | |
} | |
uint64_t encrypt(uint64_t hash, uint64_t key) { | |
DES_key_schedule ks; | |
uint64_t output; | |
DES_set_key_unchecked((DES_cblock*)&key, &ks); | |
DES_ecb_encrypt((DES_cblock*)&hash, (DES_cblock*)&output, &ks, 1); | |
return output; | |
} | |
uint64_t decrypt(uint64_t hash, uint64_t key) { | |
DES_key_schedule ks; | |
uint64_t output; | |
DES_set_key_unchecked((DES_cblock*)&key, &ks); | |
DES_ecb_encrypt((DES_cblock*)&hash, (DES_cblock*)&output, &ks, 0); | |
return output; | |
} | |
uint64_t get(uint64_t t) { | |
uint64_t ind = t&MASK_FULL; | |
return (targets[ind>>2]>>((ind&MASK_2)<<1))&MASK_2; | |
} | |
void add(uint64_t t, uint64_t i) { | |
uint64_t ind = t&MASK_FULL; | |
targets[ind>>2] |= i<<((ind&MASK_2)<<1); | |
} | |
uint64_t counter_to_key(uint64_t n) { | |
constexpr int INPUT_BYTES = 7; | |
constexpr int INPUT_BYTE_BITS = 7; | |
constexpr int BITS_MASK = (1 << INPUT_BYTE_BITS) - 1; | |
constexpr int BYTE_BITS = 8; | |
uint64_t key = 0; | |
for (uint64_t i = 0; i < INPUT_BYTES; ++i) { | |
const uint64_t input_shift = i * INPUT_BYTE_BITS; | |
const uint64_t byte_mask = BITS_MASK << input_shift; | |
const uint64_t input_byte = (n & byte_mask) >> input_shift; | |
const uint64_t output_byte = input_byte << 1; // ignore last bit | |
const uint64_t output_shift = i * BYTE_BITS; | |
key |= output_byte << output_shift; | |
} | |
return key; | |
} | |
uint64_t rand64() { | |
return ((uint64_t)rand() << 32) | rand(); | |
} | |
void expand() { | |
struct entry_t {uint64_t val; uint64_t lvl;}; | |
std::vector<struct entry_t> to_expand; | |
uint64_t ctr = 0; | |
while (ctr < N_TARGET) { | |
for (uint64_t ki = 0; ki < key_set.size(); ++ki) { | |
key_set[ki] = rand64(); | |
} | |
to_expand.push_back({.val = end, .lvl = 0}); | |
while (to_expand.size() != 0 and ctr < N_TARGET) { | |
struct entry_t target = to_expand.back(); | |
to_expand.pop_back(); | |
if (target.lvl == MAX_DEPTH) { | |
continue; | |
} | |
for (uint64_t ki = 0; ki < key_set.size(); ++ki) { | |
uint64_t key = key_set[ki]; | |
uint64_t new_target = decrypt(target.val, key); | |
if (get(new_target) == 0) { | |
add(new_target, ki + 1); | |
to_expand.push_back({.val = new_target, .lvl = target.lvl + 1}); | |
ctr += 1; | |
} | |
} | |
} | |
if (ctr < N_TARGET) { | |
std::cout << "only got " << ctr << " targets of " << N_TARGET << std::endl | |
<< "restarting with new keys" << std::endl; | |
std::fill(targets.begin(), targets.end(), 0); | |
ctr = 0; | |
} | |
} | |
} | |
uint64_t is_valid_path(uint64_t k1, std::vector<uint64_t>& sol) { | |
uint64_t cur = encrypt(start, k1); | |
for (uint64_t i = 0; i < MAX_DEPTH; ++i) { | |
uint64_t entry = get(cur); | |
if (entry == 0) { | |
break; | |
} | |
sol[i] = entry - 1; | |
uint64_t k = key_set[entry - 1]; | |
cur = encrypt(cur, k); | |
if (cur == end) { | |
return i + 1; | |
} | |
} | |
return 0; | |
} | |
void search_collision(uint64_t start, uint64_t steps) { | |
std::vector<uint64_t> sol(MAX_DEPTH); | |
for (uint64_t counter = start; counter < start + steps; ++counter) { | |
uint64_t key = counter_to_key(counter); | |
uint64_t len = is_valid_path(key, sol); | |
if (len != 0) { | |
std::cout << "done. first key is " << key << std::endl; | |
for (uint64_t j = 0; j < len; ++j) { | |
std::cout << sol[j] << ","; | |
} | |
std::cout << std::endl; | |
exit(0); | |
} | |
} | |
} | |
void find_collision(const int num_threads, const int show_progress_every) { | |
const uint64_t steps = show_progress_every / num_threads; | |
uint64_t counter = 0; | |
while (true) { | |
SpeedMeasurer speed; | |
std::vector<std::thread> threads; | |
for (int thread = 0; thread < num_threads; ++thread) { | |
auto search_thread = [counter, steps]() { | |
search_collision(counter, steps); | |
}; | |
threads.emplace_back(search_thread); | |
counter += steps; | |
speed.measure(steps); | |
} | |
for (auto& thread : threads) { | |
thread.join(); | |
} | |
std::cout << " processed " << counter << " " << speed << std::endl; | |
} | |
} | |
int main() { | |
srand(time(0)); | |
std::cout << "forward step" << std::endl; | |
expand(); | |
std::cout << "keys are" << std::endl; | |
for (uint64_t ki = 0; ki < key_set.size(); ++ki) { | |
std::cout << key_set[ki] << std::endl; | |
} | |
std::cout << "backward step" << std::endl; | |
find_collision(1,50000000); | |
return 1; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//-O2 -std=c++17 -lpthread -lcrypto | |
// 4 GB - 1 hour | |
#include <openssl/des.h> | |
#include <iostream> | |
#include <vector> | |
#include <cstring> | |
#include <thread> | |
constexpr uint64_t flip_endian(uint64_t x) { | |
return __builtin_bswap64(x); | |
} | |
uint64_t start = flip_endian(0x7765616B68617368); | |
uint64_t end = flip_endian(0xda99d1ea64144f3e); | |
#define N_TARGET (1ul << 32) | |
#define N_TOT (N_TARGET << 3) | |
#define N_BYTE (N_TOT >> 3) | |
#define MAX_DEPTH 30 | |
#define MASK_FULL (N_TOT - 1) | |
#define MASK_3 0x7 | |
std::vector<uint8_t> targets(N_BYTE,0); | |
std::vector<uint64_t> key_set(3); | |
// Usage: | |
// SpeedMeasurer speed; | |
// // ... | |
// speed.measure(items_processed); | |
// // ... | |
// cout << speed; | |
struct SpeedMeasurer { | |
std::chrono::high_resolution_clock::time_point start; | |
uint64_t items; | |
SpeedMeasurer() : start{std::chrono::high_resolution_clock::now()}, items{0} {} | |
void measure(uint64_t items_processed) { items += items_processed; } | |
double items_per_second() const { | |
const auto stop = std::chrono::high_resolution_clock::now(); | |
using seconds_t = std::chrono::duration<double, std::chrono::seconds::period>; | |
const seconds_t seconds = stop - start; | |
return static_cast<double>(items) / seconds.count(); | |
} | |
}; | |
std::ostream& operator<<(std::ostream& os, const SpeedMeasurer& speed) { | |
os << speed.items_per_second() << "/s"; | |
return os; | |
} | |
uint64_t encrypt(uint64_t hash, uint64_t key) { | |
DES_key_schedule ks; | |
uint64_t output; | |
DES_set_key_unchecked((DES_cblock*)&key, &ks); | |
DES_ecb_encrypt((DES_cblock*)&hash, (DES_cblock*)&output, &ks, 1); | |
return output; | |
} | |
uint64_t decrypt(uint64_t hash, uint64_t key) { | |
DES_key_schedule ks; | |
uint64_t output; | |
DES_set_key_unchecked((DES_cblock*)&key, &ks); | |
DES_ecb_encrypt((DES_cblock*)&hash, (DES_cblock*)&output, &ks, 0); | |
return output; | |
} | |
uint64_t get(uint64_t t) { | |
uint64_t ind = t&MASK_FULL; | |
return (targets[ind>>3]>>(ind&MASK_3))&1; | |
} | |
void add(uint64_t t) { | |
uint64_t ind = t&MASK_FULL; | |
targets[ind>>3] |= 1<<(ind&MASK_3); | |
} | |
uint64_t counter_to_key(uint64_t n) { | |
constexpr int INPUT_BYTES = 7; | |
constexpr int INPUT_BYTE_BITS = 7; | |
constexpr int BITS_MASK = (1 << INPUT_BYTE_BITS) - 1; | |
constexpr int BYTE_BITS = 8; | |
uint64_t key = 0; | |
for (uint64_t i = 0; i < INPUT_BYTES; ++i) { | |
const uint64_t input_shift = i * INPUT_BYTE_BITS; | |
const uint64_t byte_mask = BITS_MASK << input_shift; | |
const uint64_t input_byte = (n & byte_mask) >> input_shift; | |
const uint64_t output_byte = input_byte << 1; // ignore last bit | |
const uint64_t output_shift = i * BYTE_BITS; | |
key |= output_byte << output_shift; | |
} | |
return key; | |
} | |
uint64_t rand64() { | |
return ((uint64_t)rand() << 32) | rand(); | |
} | |
void expand() { | |
struct entry_t {uint64_t val; uint64_t lvl;}; | |
std::vector<struct entry_t> to_expand; | |
uint64_t ctr = 0; | |
while (ctr < N_TARGET) { | |
for (uint64_t ki = 0; ki < key_set.size(); ++ki) { | |
key_set[ki] = rand64(); | |
} | |
to_expand.push_back({.val = end, .lvl = 0}); | |
while (to_expand.size() != 0 and ctr < N_TARGET) { | |
struct entry_t target = to_expand.back(); | |
to_expand.pop_back(); | |
if (target.lvl == MAX_DEPTH) { | |
continue; | |
} | |
for (uint64_t ki = 0; ki < key_set.size(); ++ki) { | |
uint64_t key = key_set[ki]; | |
uint64_t new_target = decrypt(target.val, key); | |
if (get(new_target) == 0) { | |
add(new_target); | |
to_expand.push_back({.val = new_target, .lvl = target.lvl + 1}); | |
ctr += 1; | |
} | |
} | |
} | |
if (ctr < N_TARGET) { | |
std::cout << "only got " << ctr << " targets of " << N_TARGET << std::endl | |
<< "restarting with new keys" << std::endl; | |
std::fill(targets.begin(), targets.end(), 0); | |
ctr = 0; | |
} | |
} | |
} | |
std::vector<uint64_t> is_valid_path(uint64_t k1) { | |
struct entry_t {uint64_t val; uint64_t lvl; std::vector<uint64_t> path;}; | |
std::vector<uint64_t> empty; | |
uint64_t cur = encrypt(start, k1); | |
if (get(cur) == 0) { | |
return empty; | |
} | |
std::vector<struct entry_t> to_expand; | |
to_expand.push_back({.val = cur, .lvl = 0}); | |
while (to_expand.size() != 0) { | |
struct entry_t target = to_expand.back(); | |
to_expand.pop_back(); | |
if (target.lvl == MAX_DEPTH) { | |
continue; | |
} | |
for (uint64_t ki = 0; ki < key_set.size(); ++ki) { | |
uint64_t key = key_set[ki]; | |
uint64_t new_target = encrypt(target.val, key); | |
if (new_target == end) { | |
target.path.push_back(ki); | |
return target.path; | |
} | |
if (get(new_target) == 1) { | |
to_expand.push_back({.val = new_target, .lvl = target.lvl + 1, .path = target.path}); | |
to_expand.back().path.push_back(ki); | |
} | |
} | |
} | |
return empty; | |
} | |
void search_collision(uint64_t start, uint64_t steps) { | |
for (uint64_t counter = start; counter < start + steps; ++counter) { | |
uint64_t key = counter_to_key(counter); | |
std::vector<uint64_t> sol = is_valid_path(key); | |
if (sol.size() != 0) { | |
std::cout << "done. first key is " << key << std::endl; | |
for (uint64_t j = 0; j < sol.size(); ++j) { | |
std::cout << sol[j] << ","; | |
} | |
std::cout << std::endl; | |
exit(0); | |
} | |
} | |
} | |
void find_collision(const int num_threads, const int show_progress_every) { | |
const uint64_t steps = show_progress_every / num_threads; | |
uint64_t counter = 0; | |
while (true) { | |
SpeedMeasurer speed; | |
std::vector<std::thread> threads; | |
for (int thread = 0; thread < num_threads; ++thread) { | |
auto search_thread = [counter, steps]() { | |
search_collision(counter, steps); | |
}; | |
threads.emplace_back(search_thread); | |
counter += steps; | |
speed.measure(steps); | |
} | |
for (auto& thread : threads) { | |
thread.join(); | |
} | |
std::cout << " processed " << counter << " " << speed << std::endl; | |
} | |
} | |
int main() { | |
srand(time(0)); | |
std::cout << "forward step" << std::endl; | |
expand(); | |
std::cout << "keys are" << std::endl; | |
for (uint64_t ki = 0; ki < key_set.size(); ++ki) { | |
std::cout << key_set[ki] << std::endl; | |
} | |
std::cout << "backward step" << std::endl; | |
find_collision(1,50000000); | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment