mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-11 18:29:22 -07:00
CUDA implementation of mfulc_des_brute
This commit is contained in:
@@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
|
||||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
||||
|
||||
## [unreleased][unreleased]
|
||||
- Added CUDA version of tools/mfulc_des_brute (@C2Pwn)
|
||||
- Added `hf mfu desbrute` command: native client support for ULC key recovery (@C2Pwn)
|
||||
- Added `hf mf sen` command: native client support for FM11RF08S SEN recovery (@C2Pwn)
|
||||
- Added Makefile `PLATFORM_FILE` variable to specify another Makefile.platform file (@doegox)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
NVCC ?= nvcc
|
||||
TARGET ?= mfulc_des_brute_cuda
|
||||
SOURCES := main.cu desbrute.cu
|
||||
|
||||
NVCCFLAGS ?= -O3 -std=c++14
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SOURCES) desbrute.h des_host.h
|
||||
$(NVCC) $(NVCCFLAGS) -o $@ $(SOURCES)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* des_host.h — Self-contained CPU DES implementation (no OpenSSL, no CUDA).
|
||||
* Used for LFSR detection before GPU launch.
|
||||
*
|
||||
* All uint64_t values are "big-endian packed": byte 0 of the DES block occupies
|
||||
* bits 63-56, byte 7 occupies bits 7-0. This matches our GPU DES output format
|
||||
* exactly, so the same LFSR / rotation checks work on both sides without bswap.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* DES constant tables (1-indexed bit positions, MSB = bit 1 = our bit 0) */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static const uint8_t H_PC1[56] = {
|
||||
57,49,41,33,25,17, 9, 1, 58,50,42,34,26,18,
|
||||
10, 2,59,51,43,35,27,19, 11, 3,60,52,44,36,
|
||||
63,55,47,39,31,23,15, 7, 62,54,46,38,30,22,
|
||||
14, 6,61,53,45,37,29,21, 13, 5,28,20,12, 4
|
||||
};
|
||||
|
||||
static const uint8_t H_PC2[48] = {
|
||||
14,17,11,24, 1, 5, 3,28,15, 6,21,10,
|
||||
23,19,12, 4,26, 8, 16, 7,27,20,13, 2,
|
||||
41,52,31,37,47,55, 30,40,51,45,33,48,
|
||||
44,49,39,56,34,53, 46,42,50,36,29,32
|
||||
};
|
||||
|
||||
static const uint8_t H_SHIFTS[16] = {
|
||||
1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1
|
||||
};
|
||||
|
||||
static const uint8_t H_IP[64] = {
|
||||
58,50,42,34,26,18,10, 2, 60,52,44,36,28,20,12, 4,
|
||||
62,54,46,38,30,22,14, 6, 64,56,48,40,32,24,16, 8,
|
||||
57,49,41,33,25,17, 9, 1, 59,51,43,35,27,19,11, 3,
|
||||
61,53,45,37,29,21,13, 5, 63,55,47,39,31,23,15, 7
|
||||
};
|
||||
|
||||
static const uint8_t H_FP[64] = {
|
||||
40, 8,48,16,56,24,64,32, 39, 7,47,15,55,23,63,31,
|
||||
38, 6,46,14,54,22,62,30, 37, 5,45,13,53,21,61,29,
|
||||
36, 4,44,12,52,20,60,28, 35, 3,43,11,51,19,59,27,
|
||||
34, 2,42,10,50,18,58,26, 33, 1,41, 9,49,17,57,25
|
||||
};
|
||||
|
||||
static const uint8_t H_E[48] = {
|
||||
32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
|
||||
8, 9,10,11,12,13, 12,13,14,15,16,17,
|
||||
16,17,18,19,20,21, 20,21,22,23,24,25,
|
||||
24,25,26,27,28,29, 28,29,30,31,32, 1
|
||||
};
|
||||
|
||||
static const uint8_t H_P[32] = {
|
||||
16, 7,20,21,29,12,28,17,
|
||||
1,15,23,26, 5,18,31,10,
|
||||
2, 8,24,14,32,27, 3, 9,
|
||||
19,13,30, 6,22,11, 4,25
|
||||
};
|
||||
|
||||
static const uint8_t H_SBOX[8][4][16] = {
|
||||
{ {14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7}, /* S1 */
|
||||
{0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8},
|
||||
{4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0},
|
||||
{15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13} },
|
||||
{ {15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10}, /* S2 */
|
||||
{3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5},
|
||||
{0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15},
|
||||
{13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9} },
|
||||
{ {10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8}, /* S3 */
|
||||
{13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1},
|
||||
{13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7},
|
||||
{1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12} },
|
||||
{ {7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15}, /* S4 */
|
||||
{13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9},
|
||||
{10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4},
|
||||
{3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14} },
|
||||
{ {2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9}, /* S5 */
|
||||
{14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6},
|
||||
{4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14},
|
||||
{11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3} },
|
||||
{ {12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11}, /* S6 */
|
||||
{10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8},
|
||||
{9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6},
|
||||
{4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13} },
|
||||
{ {4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1}, /* S7 */
|
||||
{13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6},
|
||||
{1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2},
|
||||
{6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12} },
|
||||
{ {13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7}, /* S8 */
|
||||
{1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2},
|
||||
{7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8},
|
||||
{2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11} }
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Utility */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Convert 8-byte array (big-endian) → packed uint64 (byte 0 in bits 63-56) */
|
||||
static inline uint64_t bytes_to_u64(const uint8_t *b)
|
||||
{
|
||||
return ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) |
|
||||
((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32) |
|
||||
((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
|
||||
((uint64_t)b[6] << 8) | (uint64_t)b[7];
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* CPU DES primitives */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static inline uint64_t h_perm(uint64_t src, int src_bits, const uint8_t *tbl, int n)
|
||||
{
|
||||
uint64_t dst = 0;
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
int sb = tbl[i] - 1;
|
||||
dst |= ((src >> (src_bits - 1 - sb)) & 1ULL) << (n - 1 - i);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static inline void h_des_keyschedule(uint64_t key64, uint64_t sk[16])
|
||||
{
|
||||
uint64_t key56 = h_perm(key64, 64, H_PC1, 56);
|
||||
uint32_t C = (uint32_t)(key56 >> 28) & 0x0FFFFFFFU;
|
||||
uint32_t D = (uint32_t)(key56) & 0x0FFFFFFFU;
|
||||
int i;
|
||||
for (i = 0; i < 16; i++) {
|
||||
int s = H_SHIFTS[i];
|
||||
C = ((C << s) | (C >> (28 - s))) & 0x0FFFFFFFU;
|
||||
D = ((D << s) | (D >> (28 - s))) & 0x0FFFFFFFU;
|
||||
sk[i] = h_perm(((uint64_t)C << 28) | D, 56, H_PC2, 48);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t h_des_crypt(uint64_t block, const uint64_t sk[16])
|
||||
{
|
||||
uint64_t ip = h_perm(block, 64, H_IP, 64);
|
||||
uint32_t L = (uint32_t)(ip >> 32);
|
||||
uint32_t R = (uint32_t)(ip);
|
||||
int i;
|
||||
for (i = 0; i < 16; i++) {
|
||||
uint64_t eR = h_perm((uint64_t)R, 32, H_E, 48);
|
||||
uint64_t x = eR ^ sk[i];
|
||||
uint32_t sout = 0;
|
||||
int s;
|
||||
for (s = 0; s < 8; s++) {
|
||||
uint8_t b6 = (uint8_t)((x >> (42 - s * 6)) & 0x3F);
|
||||
int row = ((b6 >> 5) & 1) * 2 + (b6 & 1);
|
||||
int col = (b6 >> 1) & 0xF;
|
||||
sout |= (uint32_t)H_SBOX[s][row][col] << (28 - s * 4);
|
||||
}
|
||||
uint32_t f = (uint32_t)h_perm((uint64_t)sout, 32, H_P, 32);
|
||||
uint32_t nR = L ^ f;
|
||||
L = R; R = nR;
|
||||
}
|
||||
return h_perm(((uint64_t)R << 32) | L, 64, H_FP, 64);
|
||||
}
|
||||
|
||||
static inline uint64_t h_des_decrypt(uint64_t block, const uint64_t sk[16])
|
||||
{
|
||||
uint64_t rsk[16];
|
||||
int i;
|
||||
for (i = 0; i < 16; i++) rsk[i] = sk[15 - i];
|
||||
return h_des_crypt(block, rsk);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* LFSR validators (input: BE uint64, byte 0 in bits 63-56) */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static inline int h_valid_lfsr_ulcg(uint64_t x64)
|
||||
{
|
||||
/* x64 is already B0=MSB; original code bswap'd from OpenSSL LE, we skip that */
|
||||
uint16_t x16 = (uint16_t)(x64 >> 48);
|
||||
x16 = (uint16_t)(x16 << 15 | ((x16 >> 1) ^ ((x16 >> 3 ^ x16 >> 4 ^ x16 >> 6) & 1)));
|
||||
if (x16 != (uint16_t)((x64 >> 32) & 0xFFFF)) return 0;
|
||||
x16 = (uint16_t)(x16 << 15 | ((x16 >> 1) ^ ((x16 >> 3 ^ x16 >> 4 ^ x16 >> 6) & 1)));
|
||||
if (x16 != (uint16_t)((x64 >> 16) & 0xFFFF)) return 0;
|
||||
x16 = (uint16_t)(x16 << 15 | ((x16 >> 1) ^ ((x16 >> 3 ^ x16 >> 4 ^ x16 >> 6) & 1)));
|
||||
if (x16 != (uint16_t)(x64 & 0xFFFF)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int h_valid_lfsr_mfc(uint64_t x64)
|
||||
{
|
||||
uint16_t x16 = (uint16_t)(x64 & 0xFFFF);
|
||||
int i;
|
||||
for (i = 0; i < 16; i++)
|
||||
x16 = (uint16_t)(x16 >> 1 | (x16 ^ x16 >> 2 ^ x16 >> 3 ^ x16 >> 5) << 15);
|
||||
if (x16 != (uint16_t)((x64 >> 16) & 0xFFFF)) return 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
x16 = (uint16_t)(x16 >> 1 | (x16 ^ x16 >> 2 ^ x16 >> 3 ^ x16 >> 5) << 15);
|
||||
if (x16 != (uint16_t)((x64 >> 32) & 0xFFFF)) return 0;
|
||||
for (i = 0; i < 16; i++)
|
||||
x16 = (uint16_t)(x16 >> 1 | (x16 ^ x16 >> 2 ^ x16 >> 3 ^ x16 >> 5) << 15);
|
||||
if (x16 != (uint16_t)((x64 >> 48) & 0xFFFF)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect LFSR type from init_ciphertext (8 bytes).
|
||||
* Decrypts with the all-zero DES key and checks LFSR structure.
|
||||
* Returns 1=ULCG, 2=MFC, 0=unknown.
|
||||
*/
|
||||
static inline int h_detect_lfsr(const uint8_t init_ciphertext[8])
|
||||
{
|
||||
uint8_t zero_key[8];
|
||||
uint64_t sk[16];
|
||||
uint64_t ct, out;
|
||||
memset(zero_key, 0, 8);
|
||||
h_des_keyschedule(bytes_to_u64(zero_key), sk);
|
||||
ct = bytes_to_u64(init_ciphertext);
|
||||
out = h_des_decrypt(ct, sk);
|
||||
if (h_valid_lfsr_ulcg(out)) return 1;
|
||||
if (h_valid_lfsr_mfc(out)) return 2;
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* desbrute.h — Shared types and GPU launch interface.
|
||||
* No external dependencies beyond CUDA runtime.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define BLOCK_SIZE 8
|
||||
#define KEY_SIZE 16
|
||||
|
||||
#define LFSR_UNDEF 0
|
||||
#define LFSR_ULCG 1
|
||||
#define LFSR_MFC 2
|
||||
|
||||
/* Metrics returned by desbrute_launch */
|
||||
typedef struct {
|
||||
int found; /* 1 if a key was found */
|
||||
uint32_t idx; /* winning candidate index (if found) */
|
||||
double wall_ms; /* wall-clock time in milliseconds */
|
||||
float gpu_ms; /* GPU kernel time (CUDA events) */
|
||||
uint64_t candidates_tested; /* total candidates processed before exit */
|
||||
double keys_per_sec; /* throughput: candidates / gpu_sec */
|
||||
} desbrute_result_t;
|
||||
|
||||
/*
|
||||
* desbrute_launch — brute-force 2^28 candidate key indices on the GPU.
|
||||
*
|
||||
* All uint64_t block values are big-endian packed (byte 0 in bits 63-56).
|
||||
* The caller must have already called cudaSetDevice() if needed.
|
||||
*/
|
||||
void desbrute_launch(
|
||||
uint64_t ciphertext_be,
|
||||
uint64_t init_ciphertext_be,
|
||||
uint64_t prev_ciphertext_be,
|
||||
const uint8_t base_key[KEY_SIZE],
|
||||
int key_mode,
|
||||
int lfsr_type,
|
||||
int is_reader_mode,
|
||||
desbrute_result_t *result
|
||||
);
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* main.cu - CUDA-accelerated 2TDEA key recovery.
|
||||
* Written by C2Pwn
|
||||
*
|
||||
* Usage
|
||||
* -----
|
||||
* Counterfeit mode (-c):
|
||||
* desbrute -c <ERndB_null_16hex> <ERndB_target_16hex> <key_32hex> <seg 1-4> [device]
|
||||
*
|
||||
* Reader nonce mode (-r):
|
||||
* desbrute -r <ERndB_16hex> <ERndARndBprime_32hex> <key_32hex> <seg 1-4> [device]
|
||||
*
|
||||
* [device] is optional. When omitted the best available GPU is chosen
|
||||
* automatically (highest SM count). Pass an integer to force a specific
|
||||
* CUDA device index.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "des_host.h"
|
||||
#include "desbrute.h"
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Helpers */
|
||||
/* ========================================================================== */
|
||||
|
||||
static int hex_to_bytes(const char *hex, uint8_t *buf, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
if (strlen(hex) != len * 2) return 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
unsigned int byte;
|
||||
if (sscanf(hex + 2 * i, "%2x", &byte) != 1) return 0;
|
||||
buf[i] = (uint8_t)byte;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_hex(const uint8_t *buf, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < len; i++) printf("%02X", buf[i]);
|
||||
}
|
||||
|
||||
static void print_separator(void)
|
||||
{
|
||||
printf("---------------------------------------------------------------\n");
|
||||
}
|
||||
|
||||
static void print_usage(const char *name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"\nUsage:\n"
|
||||
" Counterfeit: %s -c <ERndB_null_16hex> <ERndB_target_16hex>"
|
||||
" <key_32hex> <seg 1-4> [device]\n"
|
||||
" Reader nonce: %s -r <ERndB_16hex> <ERndARndBprime_32hex>"
|
||||
" <key_32hex> <seg 1-4> [device]\n"
|
||||
"\n"
|
||||
" seg : 1-4 (which 4-byte block of the 16-byte key to brute-force)\n"
|
||||
" device : optional CUDA device index (default: auto-select best GPU)\n"
|
||||
"\n",
|
||||
name, name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Auto device selection */
|
||||
/* ========================================================================== */
|
||||
|
||||
/*
|
||||
* Pick the CUDA device with the most streaming multiprocessors.
|
||||
* On a single-GPU system this always returns 0.
|
||||
* On multi-GPU systems it chooses the most powerful card.
|
||||
* Prints a one-line summary of all available devices.
|
||||
*/
|
||||
static int pick_best_device(void)
|
||||
{
|
||||
int count = 0;
|
||||
if (cudaGetDeviceCount(&count) != cudaSuccess || count == 0) {
|
||||
fprintf(stderr, "[cuda] No CUDA devices found.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int best_dev = 0, best_mp = 0;
|
||||
int d;
|
||||
for (d = 0; d < count; d++) {
|
||||
struct cudaDeviceProp p;
|
||||
if (cudaGetDeviceProperties(&p, d) != cudaSuccess) continue;
|
||||
printf(" [dev %d] %s SM %d.%d %d MPs\n",
|
||||
d, p.name, p.major, p.minor, p.multiProcessorCount);
|
||||
if (p.multiProcessorCount > best_mp) {
|
||||
best_mp = p.multiProcessorCount;
|
||||
best_dev = d;
|
||||
}
|
||||
}
|
||||
return best_dev;
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* GPU info banner */
|
||||
/* ========================================================================== */
|
||||
|
||||
static void print_gpu_info(int dev)
|
||||
{
|
||||
struct cudaDeviceProp p;
|
||||
if (cudaGetDeviceProperties(&p, dev) != cudaSuccess) return;
|
||||
|
||||
double bw_gbps = 2.0 * p.memoryClockRate * 1e3
|
||||
* p.memoryBusWidth / 8.0 / 1e9;
|
||||
|
||||
/* Approximate CUDA core count — varies by architecture */
|
||||
int cores_per_mp =
|
||||
(p.major == 9) ? 128 :
|
||||
(p.major == 8 && p.minor == 9) ? 128 :
|
||||
(p.major == 8 && p.minor == 6) ? 128 :
|
||||
(p.major == 8 && p.minor == 0) ? 64 :
|
||||
(p.major == 7 && p.minor == 5) ? 64 :
|
||||
(p.major == 7) ? 64 :
|
||||
(p.major == 6 && p.minor == 1) ? 128 :
|
||||
(p.major == 6 && p.minor == 0) ? 64 :
|
||||
(p.major == 5) ? 128 : 64;
|
||||
|
||||
print_separator();
|
||||
printf(" GPU : %s [device %d]\n", p.name, dev);
|
||||
printf(" SM : %d.%d | MPs: %d | CUDA cores: ~%d\n",
|
||||
p.major, p.minor, p.multiProcessorCount,
|
||||
p.multiProcessorCount * cores_per_mp);
|
||||
printf(" Clock: %d MHz (mem: %d MHz, bus: %d-bit)\n",
|
||||
p.clockRate / 1000, p.memoryClockRate / 1000, p.memoryBusWidth);
|
||||
printf(" VRAM : %.0f MB | Peak BW: %.0f GB/s\n",
|
||||
(double)p.totalGlobalMem / (1024.0 * 1024.0), bw_gbps);
|
||||
printf(" L2 : %d KB | Warp size: %d\n",
|
||||
p.l2CacheSize / 1024, p.warpSize);
|
||||
print_separator();
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* main */
|
||||
/* ========================================================================== */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int is_reader_mode, seg, key_mode, cuda_dev, lfsr_type;
|
||||
uint8_t init_ciphertext[BLOCK_SIZE];
|
||||
uint8_t tmp_blocks[2 * BLOCK_SIZE];
|
||||
uint8_t ciphertext[BLOCK_SIZE];
|
||||
uint8_t prev_ciphertext[BLOCK_SIZE];
|
||||
uint8_t base_key[KEY_SIZE];
|
||||
desbrute_result_t result;
|
||||
|
||||
/* --- Argument parsing ------------------------------------------------- */
|
||||
if (argc < 2) print_usage(argv[0]);
|
||||
|
||||
if (strcmp(argv[1], "-c") == 0) {
|
||||
is_reader_mode = 0;
|
||||
if (argc < 6 || argc > 7) {
|
||||
fprintf(stderr, "Error: -c requires exactly 4 arguments"
|
||||
" plus an optional device index.\n");
|
||||
print_usage(argv[0]);
|
||||
}
|
||||
} else if (strcmp(argv[1], "-r") == 0) {
|
||||
is_reader_mode = 1;
|
||||
if (argc < 6 || argc > 7) {
|
||||
fprintf(stderr, "Error: -r requires exactly 4 arguments"
|
||||
" plus an optional device index.\n");
|
||||
print_usage(argv[0]);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Error: first argument must be -c or -r\n");
|
||||
print_usage(argv[0]);
|
||||
}
|
||||
|
||||
/* --- Parse fixed positional args ------------------------------------- */
|
||||
if (is_reader_mode) {
|
||||
if (!hex_to_bytes(argv[2], init_ciphertext, BLOCK_SIZE)) {
|
||||
fprintf(stderr, "Error: ERndB must be exactly 16 hex chars (8 bytes)\n");
|
||||
return 1;
|
||||
}
|
||||
if (!hex_to_bytes(argv[3], tmp_blocks, 2 * BLOCK_SIZE)) {
|
||||
fprintf(stderr, "Error: ERndA||RndB' must be exactly 32 hex chars (16 bytes)\n");
|
||||
return 1;
|
||||
}
|
||||
memcpy(prev_ciphertext, tmp_blocks, BLOCK_SIZE);
|
||||
memcpy(ciphertext, tmp_blocks + BLOCK_SIZE, BLOCK_SIZE);
|
||||
} else {
|
||||
if (!hex_to_bytes(argv[2], init_ciphertext, BLOCK_SIZE)) {
|
||||
fprintf(stderr, "Error: null-key ERndB must be exactly 16 hex chars (8 bytes)\n");
|
||||
return 1;
|
||||
}
|
||||
if (!hex_to_bytes(argv[3], ciphertext, BLOCK_SIZE)) {
|
||||
fprintf(stderr, "Error: target ERndB must be exactly 16 hex chars (8 bytes)\n");
|
||||
return 1;
|
||||
}
|
||||
memset(prev_ciphertext, 0, BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (!hex_to_bytes(argv[4], base_key, KEY_SIZE)) {
|
||||
fprintf(stderr, "Error: base key must be exactly 32 hex chars (16 bytes)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
seg = atoi(argv[5]);
|
||||
if (seg < 1 || seg > 4) {
|
||||
fprintf(stderr, "Error: segment must be 1, 2, 3, or 4\n");
|
||||
return 1;
|
||||
}
|
||||
key_mode = seg - 1;
|
||||
|
||||
/* --- CUDA device selection -------------------------------------------- */
|
||||
print_separator();
|
||||
printf(" DESBRUTE - CUDA 2TDEA key recovery engine\n");
|
||||
print_separator();
|
||||
|
||||
if (argc == 7) {
|
||||
/* Explicit device requested */
|
||||
cuda_dev = atoi(argv[6]);
|
||||
int count = 0;
|
||||
cudaGetDeviceCount(&count);
|
||||
if (cuda_dev < 0 || cuda_dev >= count) {
|
||||
fprintf(stderr,
|
||||
"[cuda] Device %d does not exist (%d device(s) available).\n"
|
||||
" Re-run without the [device] argument to auto-select.\n",
|
||||
cuda_dev, count);
|
||||
return 1;
|
||||
}
|
||||
printf(" Device: %d (user-specified)\n", cuda_dev);
|
||||
} else {
|
||||
/* Auto-select best device */
|
||||
printf(" Auto-selecting best CUDA device...\n");
|
||||
cuda_dev = pick_best_device();
|
||||
printf(" Selected: device %d\n", cuda_dev);
|
||||
}
|
||||
|
||||
if (cudaSetDevice(cuda_dev) != cudaSuccess) {
|
||||
fprintf(stderr, "[cuda] Failed to set device %d\n", cuda_dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* --- LFSR detection (counterfeit mode only, CPU) ---------------------- */
|
||||
lfsr_type = LFSR_UNDEF;
|
||||
if (!is_reader_mode) {
|
||||
lfsr_type = h_detect_lfsr(init_ciphertext);
|
||||
switch (lfsr_type) {
|
||||
case LFSR_ULCG:
|
||||
printf("[lfsr] Detected: ULCG\n"); break;
|
||||
case LFSR_MFC:
|
||||
printf("[lfsr] Detected: MFC (USCUID-UL / FJ8010)\n"); break;
|
||||
default:
|
||||
fprintf(stderr, "[lfsr] ERROR: unrecognised LFSR - cannot proceed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Banner ----------------------------------------------------------- */
|
||||
print_gpu_info(cuda_dev);
|
||||
printf(" Mode : %s\n", is_reader_mode ? "Reader nonce (-r)" : "Counterfeit (-c)");
|
||||
printf(" Segment : %d (key_mode %d, bytes %d-%d of full key)\n",
|
||||
seg, key_mode, key_mode * 4, key_mode * 4 + 3);
|
||||
printf(" Base key : "); print_hex(base_key, KEY_SIZE); printf("\n");
|
||||
printf(" Ciphertext : "); print_hex(ciphertext, BLOCK_SIZE); printf("\n");
|
||||
printf(" Init CT : "); print_hex(init_ciphertext, BLOCK_SIZE); printf("\n");
|
||||
if (is_reader_mode) {
|
||||
printf(" Prev CT : "); print_hex(prev_ciphertext, BLOCK_SIZE); printf("\n");
|
||||
}
|
||||
print_separator();
|
||||
fflush(stdout);
|
||||
|
||||
/* --- Launch GPU search ------------------------------------------------ */
|
||||
uint64_t ct_be = bytes_to_u64(ciphertext);
|
||||
uint64_t init_be = bytes_to_u64(init_ciphertext);
|
||||
uint64_t prev_be = bytes_to_u64(prev_ciphertext);
|
||||
|
||||
desbrute_launch(ct_be, init_be, prev_be,
|
||||
base_key, key_mode, lfsr_type, is_reader_mode,
|
||||
&result);
|
||||
|
||||
/* --- Metrics ---------------------------------------------------------- */
|
||||
print_separator();
|
||||
printf(" PERFORMANCE METRICS\n");
|
||||
print_separator();
|
||||
printf(" GPU kernel time : %.3f ms\n", result.gpu_ms);
|
||||
printf(" Wall-clock time : %.3f ms\n", result.wall_ms);
|
||||
printf(" Candidates : %llu / %llu\n",
|
||||
(unsigned long long)result.candidates_tested,
|
||||
(unsigned long long)(1ULL << 28));
|
||||
printf(" Throughput : %.2f M keys/s\n", result.keys_per_sec / 1e6);
|
||||
{
|
||||
double overhead_pct = (result.wall_ms > 0)
|
||||
? 100.0 * (result.wall_ms - result.gpu_ms) / result.wall_ms
|
||||
: 0.0;
|
||||
printf(" Host overhead : %.1f%%\n", overhead_pct);
|
||||
}
|
||||
print_separator();
|
||||
|
||||
/* --- Result ----------------------------------------------------------- */
|
||||
if (!result.found) {
|
||||
printf(" RESULT: No matching key found.\n");
|
||||
print_separator();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Reconstruct full key from winning index */
|
||||
uint8_t b0 = (uint8_t)(( result.idx & 0x7Fu) << 1);
|
||||
uint8_t b1 = (uint8_t)(((result.idx >> 7) & 0x7Fu) << 1);
|
||||
uint8_t b2 = (uint8_t)(((result.idx >> 14) & 0x7Fu) << 1);
|
||||
uint8_t b3 = (uint8_t)(((result.idx >> 21) & 0x7Fu) << 1);
|
||||
|
||||
uint8_t full_key[KEY_SIZE];
|
||||
memcpy(full_key, base_key, KEY_SIZE);
|
||||
{
|
||||
int seg_offset = key_mode * 4;
|
||||
full_key[seg_offset] = b0;
|
||||
full_key[seg_offset + 1] = b1;
|
||||
full_key[seg_offset + 2] = b2;
|
||||
full_key[seg_offset + 3] = b3;
|
||||
}
|
||||
|
||||
printf(" RESULT: KEY FOUND\n");
|
||||
printf(" Index : %u (0x%08X)\n", result.idx, result.idx);
|
||||
printf(" Full key: "); print_hex(full_key, KEY_SIZE); printf("\n");
|
||||
print_separator();
|
||||
|
||||
cudaDeviceReset();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user