99 * Will try to find a public key starting with: ABCDEF
1010 */
1111
12+ #include <inttypes.h>
13+ #include <stdbool.h>
1214#include <stdlib.h>
1315#include <stdio.h>
1416#include <string.h>
17+ #include <time.h>
1518
1619/* Sodium includes*/
1720#include <sodium/crypto_scalarmult_curve25519.h>
1821#include <sodium/randombytes.h>
1922
20- #include "../../testing/misc_tools.h"
21- #include "../../toxcore/ccompat.h"
23+ #define KEY_LEN 32
24+ // Maximum number of bytes this program can crack in one run
25+ #define MAX_CRACK_BYTES 8
26+ // Maximum length of hex encoded prefix
27+ #define MAX_HEX_PREFIX_LEN (MAX_CRACK_BYTES * 2)
2228
23- static void print_key (uint8_t * client_id )
24- {
25- uint32_t j ;
29+ #if defined(_OPENMP )
30+ #include <omp.h>
31+ #define NUM_THREADS () ((unsigned) omp_get_max_threads())
32+ #else
33+ #define NUM_THREADS () (1U)
34+ #endif
2635
27- for (j = 0 ; j < 32 ; j ++ ) {
36+ static void print_key (const uint8_t * client_id )
37+ {
38+ for (uint32_t j = 0 ; j < 32 ; j ++ ) {
2839 printf ("%02X" , client_id [j ]);
2940 }
3041}
3142
43+ /// bytes needs to be at least (hex_len+1)/2 long
44+ static size_t hex_string_to_bin (const char * hex_string , size_t hex_len , uint8_t * bytes )
45+ {
46+ size_t i ;
47+ const char * pos = hex_string ;
48+ // make even
49+
50+ for (i = 0 ; i < hex_len / 2 ; ++ i , pos += 2 ) {
51+ uint8_t val ;
52+
53+ if (sscanf (pos , "%02hhx" , & val ) != 1 ) {
54+ return 0 ;
55+ }
56+
57+ bytes [i ] = val ;
58+ }
59+
60+ if (i * 2 < hex_len ) {
61+ uint8_t val ;
62+
63+ if (sscanf (pos , "%hhx" , & val ) != 1 ) {
64+ return 0 ;
65+ }
66+
67+ bytes [i ] = (uint8_t )(val << 4 );
68+ ++ i ;
69+ }
70+
71+ return i ;
72+ }
73+
74+ static size_t match_hex_prefix (const uint8_t * key , const uint8_t * prefix , size_t prefix_len )
75+ {
76+ size_t same = 0 ;
77+ uint8_t diff = 0 ;
78+ size_t i ;
79+
80+ for (i = 0 ; i < prefix_len / 2 ; i ++ ) {
81+ diff = key [i ] ^ prefix [i ];
82+
83+ // First check high nibble
84+ if ((diff & 0xF0 ) == 0 ) {
85+ same ++ ;
86+ }
87+
88+ // Then low nibble
89+ if (diff == 0 ) {
90+ same ++ ;
91+ } else {
92+ break ;
93+ }
94+ }
95+
96+ // check last high nibble
97+ if ((prefix_len % 2 ) && diff == 0 ) {
98+ diff = key [i ] ^ prefix [i ];
99+
100+ // First check high nibble
101+ if ((diff & 0xF0 ) == 0 ) {
102+ same ++ ;
103+ }
104+ }
105+
106+ return same ;
107+ }
108+
109+ static void cracker_core (uint64_t range_start , uint64_t range_end , uint64_t range_offs , uint64_t priv_key_shadow [4 ],
110+ uint32_t * longest_match , uint8_t hex_prefix [MAX_CRACK_BYTES ], size_t prefix_chars_len )
111+ {
112+ #pragma omp parallel for firstprivate(priv_key_shadow) shared(longest_match, range_start, range_end, range_offs, hex_prefix, prefix_chars_len) schedule(static) default(none)
113+
114+ for (uint64_t batch = range_start ; batch < range_end ; batch ++ ) {
115+ uint8_t * priv_key = (uint8_t * ) priv_key_shadow ;
116+ /*
117+ * We can't use the first and last bytes because they are masked in
118+ * curve25519. Offset by 16 bytes to get better alignment.
119+ */
120+ uint64_t * counter = priv_key_shadow + 2 ;
121+ /*
122+ * Add to `counter` instead of assign here, to preservere more randomness on short runs
123+ * There can be an intentional overflow in `batch + range_offs`
124+ */
125+ * counter += batch + range_offs ;
126+ uint8_t pub_key [KEY_LEN ] = {0 };
127+
128+ crypto_scalarmult_curve25519_base (pub_key , priv_key );
129+
130+ const unsigned matching = (unsigned ) match_hex_prefix (pub_key , hex_prefix , prefix_chars_len );
131+
132+ // Global compare and update
133+ uint32_t l_longest_match ;
134+ #pragma omp atomic read
135+ l_longest_match = * longest_match ;
136+
137+ if (matching > l_longest_match ) {
138+ #pragma omp atomic write
139+ * longest_match = matching ;
140+
141+ #pragma omp critical
142+ {
143+ printf ("%u chars matching: \n" , matching );
144+ printf ("Public key: " );
145+ print_key (pub_key );
146+ printf ("\nSecret key: " );
147+ print_key (priv_key );
148+ printf ("\n" );
149+ }
150+ }
151+ }
152+ }
153+
154+ void print_stats (double seconds_passed , double keys_tried )
155+ {
156+ printf ("Runtime: %10lus, Keys tried %e/%e, Calculating %e keys/s\n" ,
157+ (unsigned long ) seconds_passed , keys_tried , (double ) UINT64_MAX , keys_tried / seconds_passed );
158+ }
32159
33160int main (int argc , char * argv [])
34161{
@@ -37,43 +164,89 @@ int main(int argc, char *argv[])
37164 return 0 ;
38165 }
39166
40- long long unsigned int num_tries = 0 ;
167+ const size_t prefix_chars_len = strlen (argv [1 ]);
168+
169+ /*
170+ * If you can afford the hardware to crack longer prefixes, you can probably
171+ * afford to rewrite this program.
172+ */
173+ if (prefix_chars_len > MAX_HEX_PREFIX_LEN ) {
174+ printf ("Finding a key with more than 16 hex chars as prefix is not supported\n" );
175+ return 1 ;
176+ }
177+
178+ uint8_t hex_prefix [MAX_CRACK_BYTES ] = {0 };
41179
42- uint32_t len = strlen (argv [1 ]) / 2 ;
43- unsigned char * key = hex_string_to_bin (argv [1 ]);
44- uint8_t pub_key [32 ], priv_key [32 ], c_key [32 ];
180+ const size_t prefix_len = hex_string_to_bin (argv [1 ], prefix_chars_len , hex_prefix );
45181
46- if (len > 32 ) {
47- len = 32 ;
182+ if (prefix_len == 0 ) {
183+ printf ("Invalid hex key specified\n" );
184+ return 1 ;
48185 }
49186
50- memcpy (c_key , key , len );
51- free (key );
52- randombytes (priv_key , 32 );
187+ printf ("Searching for key with prefix: %s\n" , argv [1 ]);
53188
54- while (1 ) {
55- crypto_scalarmult_curve25519_base (pub_key , priv_key );
56- uint32_t i ;
189+ time_t start_time = time (NULL );
57190
58- if (memcmp (c_key , pub_key , len ) == 0 ) {
59- break ;
60- }
191+ // Declare private key bytes as uint64_t[4] so we can lower the alignment without problems
192+ uint64_t priv_key_shadow [KEY_LEN / 8 ];
193+ uint8_t * priv_key = (uint8_t * ) priv_key_shadow ;
194+ // Put randomness into the key
195+ randombytes (priv_key , KEY_LEN );
196+ uint32_t longest_match = 0 ;
61197
62- for (i = 32 ; i != 0 ; -- i ) {
63- priv_key [i - 1 ] += 1 ;
64198
65- if (priv_key [i - 1 ] != 0 ) {
66- break ;
67- }
199+ // Finishes a batch every ~10s on my PC
200+ const uint64_t batch_size = (UINT64_C (1 ) << 18 ) * NUM_THREADS ();
201+
202+ // calculate remaining batch that doesn't fit the main loop
203+ const uint64_t rem_batch_size = UINT64_MAX % batch_size ;
204+
205+ const uint64_t rem_start = UINT64_MAX - rem_batch_size - 1 ;
206+
207+ cracker_core (rem_start , UINT64_MAX , 1 , priv_key_shadow , & longest_match , hex_prefix , prefix_chars_len );
208+
209+ double seconds_passed = difftime (time (NULL ), start_time );
210+ double old_seconds_passed = seconds_passed ;
211+
212+ // Reduce time to first stats output
213+ print_stats (seconds_passed , rem_batch_size + 1 );
214+
215+ if (longest_match >= prefix_chars_len ) {
216+ printf ("Found matching prefix, exiting...\n" );
217+ return 0 ;
218+ }
219+
220+
221+ for (uint64_t tries = 0 ; tries < rem_start ; tries += batch_size ) {
222+ cracker_core (tries , tries + batch_size , 0 , priv_key_shadow , & longest_match , hex_prefix , prefix_chars_len );
223+
224+ seconds_passed = difftime (time (NULL ), start_time );
225+ // Use double type to avoid overflow in addition, we don't need precision here anyway
226+ double keys_tried = ((double ) tries ) + rem_batch_size + 1 ;
227+
228+ if (longest_match >= prefix_chars_len ) {
229+ print_stats (seconds_passed , keys_tried );
230+ printf ("Found matching prefix, exiting...\n" );
231+ return 0 ;
68232 }
69233
70- ++ num_tries ;
234+ // Rate limit output
235+ if (seconds_passed - old_seconds_passed > 5.0 ) {
236+ old_seconds_passed = seconds_passed ;
237+ print_stats (seconds_passed , keys_tried );
238+ fflush (stdout );
239+ }
71240 }
72241
73- printf ("Public key:\n" );
74- print_key (pub_key );
75- printf ("\nPrivate key:\n" );
242+ printf ("Congrats future person who successfully searched a key space of 2^64\n" );
243+ uint64_t * counter = priv_key_shadow + 2 ;
244+ * counter = 0 ;
245+ printf ("Didn't find anything from:\n" );
246+ print_key (priv_key );
247+ printf ("\nto:\n" );
248+ * counter = UINT64_MAX ;
76249 print_key (priv_key );
77- printf ("\n %llu keys tried\n" , num_tries );
78- return 0 ;
250+ printf ("\n" );
251+ return 2 ;
79252}
0 commit comments