DESCracker/native/cpu/med_cpu.cpp
Tom Butcher 8f2d3a2489 Add initial project structure for DES Key Cracker
- Created .gitignore to exclude build artifacts and dependencies.
- Added package.json and package-lock.json for project dependencies and scripts.
- Included pnpm workspace configuration for managing packages.
- Implemented TypeScript configuration in tsconfig.json.
- Added README.md with project description and usage instructions.
- Introduced native code for DES encryption and decryption in C/C++.
- Created initial decoded data structure for handling scan results.
- Established basic file structure for decoded outputs and native builds.
2026-09-19 16:10:35 +01:00

198 lines
6.1 KiB
C++

#include "../common/des.h"
#include <atomic>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
static void fail(const char *msg) {
std::fprintf(stderr, "%s\n", msg);
std::exit(2);
}
static uint64_t parse_hex_u64(const char *hex) {
const char *p = hex;
if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
p += 2;
}
return std::strtoull(p, nullptr, 16);
}
static std::map<std::string, std::string> parse_opts(int argc, char **argv) {
std::map<std::string, std::string> opts;
for (int i = 2; i + 1 < argc; i += 2) {
if (std::strncmp(argv[i], "--", 2) != 0) {
i -= 1;
continue;
}
opts[argv[i] + 2] = argv[i + 1];
}
return opts;
}
static unsigned cpu_count() {
unsigned n = std::thread::hardware_concurrency();
return n ? n : 1;
}
static void run_devices() {
unsigned n = cpu_count();
std::printf("[{\"id\":0,\"name\":\"CPU (%u cores)\",\"type\":\"cpu\",\"cores\":%u}]\n", n, n);
}
static void run_selftest() {
if (!des_selftest()) {
fail("DES self-test failed");
}
std::fprintf(stderr, "cpu_selftest ok cores=%u\n", cpu_count());
}
static void sleep_ms(int ms) {
#ifdef _WIN32
Sleep((DWORD)ms);
#else
usleep((useconds_t)ms * 1000);
#endif
}
static void run_brute(const std::map<std::string, std::string> &opts) {
uint32_t key_len = (uint32_t)std::strtoul(opts.count("key-len") ? opts.at("key-len").c_str() : "0", nullptr, 10);
std::string charset = opts.count("charset") ? opts.at("charset") : "";
uint32_t pad_byte = (uint32_t)(parse_hex_u64(opts.count("pad") ? opts.at("pad").c_str() : "00") & 0xFF);
uint64_t target = parse_hex_u64(opts.count("target") ? opts.at("target").c_str() : "0");
uint64_t start = std::strtoull(opts.count("start") ? opts.at("start").c_str() : "0", nullptr, 10);
uint64_t count = std::strtoull(opts.count("count") ? opts.at("count").c_str() : "0", nullptr, 10);
unsigned workers = (unsigned)std::strtoul(opts.count("workers") ? opts.at("workers").c_str() : "0", nullptr, 10);
if (key_len == 0 || charset.empty() || count == 0) {
fail("brute requires --key-len --charset --count");
}
if (workers == 0) {
workers = cpu_count();
}
uint64_t fills[8] = {0};
uint32_t nfills = 0;
std::string fills_str = opts.count("fills") ? opts.at("fills") : "0000000000000000";
std::string token;
for (size_t i = 0, begin = 0; i <= fills_str.size(); i++) {
if (i == fills_str.size() || fills_str[i] == ',') {
token = fills_str.substr(begin, i - begin);
if (!token.empty() && nfills < 8) {
fills[nfills++] = parse_hex_u64(token.c_str());
}
begin = i + 1;
}
}
const uint8_t *cs = reinterpret_cast<const uint8_t *>(charset.data());
uint32_t clen = (uint32_t)charset.size();
std::atomic<uint64_t> done{0};
std::mutex out_mu;
auto worker_fn = [&](uint64_t wstart, uint64_t wcount) {
uint64_t local = 0;
for (uint64_t i = 0; i < wcount; i++) {
uint64_t index = wstart + i;
uint64_t key = make_key(index, key_len, clen, pad_byte, cs);
uint64_t sk[16];
des_key_schedule(key, sk);
uint64_t pt = des_crypt(target, sk, 1);
if (is_fill(pt, fills, nfills)) {
std::lock_guard<std::mutex> lock(out_mu);
std::printf("{\"index\":%llu,\"key_hex\":\"%016llx\",\"plain_hex\":\"%016llx\"}\n",
(unsigned long long)index, (unsigned long long)key, (unsigned long long)pt);
std::fflush(stdout);
}
local++;
if ((local & 0x3FFF) == 0) {
done.fetch_add(0x4000, std::memory_order_relaxed);
local = 0;
}
}
if (local) {
done.fetch_add(local, std::memory_order_relaxed);
}
};
std::vector<std::thread> threads;
uint64_t chunk = count / workers;
uint64_t rem = count % workers;
uint64_t cursor = start;
auto t0 = std::chrono::steady_clock::now();
std::atomic<bool> finished{false};
std::thread reporter([&]() {
while (!finished.load(std::memory_order_relaxed)) {
uint64_t d = done.load(std::memory_order_relaxed);
if (d > count) {
d = count;
}
auto now = std::chrono::steady_clock::now();
double elapsed = std::chrono::duration<double>(now - t0).count();
if (elapsed < 1e-6) {
elapsed = 1e-6;
}
std::fprintf(stderr, "gpu %llu/%llu %.0f keys/s\n", (unsigned long long)d,
(unsigned long long)count, d / elapsed);
sleep_ms(200);
}
});
for (unsigned w = 0; w < workers; w++) {
uint64_t n = chunk + (w < rem ? 1 : 0);
if (n == 0) {
continue;
}
threads.emplace_back(worker_fn, cursor, n);
cursor += n;
}
for (auto &t : threads) {
t.join();
}
finished.store(true, std::memory_order_relaxed);
reporter.join();
auto now = std::chrono::steady_clock::now();
double elapsed = std::chrono::duration<double>(now - t0).count();
if (elapsed < 1e-6) {
elapsed = 1e-6;
}
std::fprintf(stderr, "gpu %llu/%llu %.0f keys/s\n", (unsigned long long)count,
(unsigned long long)count, count / elapsed);
}
int main(int argc, char **argv) {
if (argc < 2) {
fail("usage: med_cpu devices | selftest | brute [options]");
}
if (std::strcmp(argv[1], "devices") == 0) {
run_devices();
return 0;
}
auto opts = parse_opts(argc, argv);
if (std::strcmp(argv[1], "selftest") == 0) {
run_selftest();
return 0;
}
if (std::strcmp(argv[1], "brute") == 0) {
run_selftest();
run_brute(opts);
return 0;
}
fail("unknown command");
return 2;
}