4
4
#include <openssl/evp.h>
5
5
#include <openssl/hmac.h>
6
6
#include <stdbool.h>
7
- #include <time .h>
7
+ #include <pthread .h>
8
8
#include "base64.h"
9
9
10
10
char * g_header_b64 = NULL ; // Holds the Base64 header of the original JWT
@@ -26,62 +26,128 @@ size_t g_to_encrypt_len = 0;
26
26
char * g_alphabet = NULL ;
27
27
size_t g_alphabet_len = 0 ;
28
28
29
- // Holds the computed signature at each iteration to compare it with the original
30
- // signature
31
- unsigned char * g_result = NULL ;
32
- unsigned int g_result_len = 0 ;
29
+ char * g_found_secret = NULL ;
33
30
34
- char * g_buffer = NULL ;
31
+ struct s_thread_data {
32
+ EVP_MD * g_evp_md ; // The hash function to apply the HMAC to
35
33
36
- // The hash function to apply the HMAC to
37
- EVP_MD * g_evp_md = NULL ;
34
+ // Holds the computed signature at each iteration to compare it with the original
35
+ // signature
36
+ unsigned char * g_result ;
37
+ unsigned int g_result_len ;
38
+
39
+ char * g_buffer ; // Holds the secret being constructed
40
+
41
+ char starting_letter ; // Each thread is assigned a first letter
42
+ size_t max_len ; // And tries combinations up to a certain length
43
+ };
44
+
45
+ void init_thread_data (struct s_thread_data * data , char starting_letter , size_t max_len ) {
46
+ data -> max_len = max_len ;
47
+ data -> starting_letter = starting_letter ;
48
+ // The chosen hash function is SHA-256
49
+ data -> g_evp_md = (EVP_MD * ) EVP_sha256 ();
50
+ // Allocate the buffer used to hold the calculated signature
51
+ data -> g_result = malloc (EVP_MAX_MD_SIZE );
52
+ // Allocate the buffer used to hold the generated key
53
+ data -> g_buffer = malloc (max_len + 1 );
54
+ }
55
+
56
+ void destroy_thread_data (struct s_thread_data * data ) {
57
+ free (data -> g_result );
58
+ free (data -> g_buffer );
59
+ }
38
60
39
61
/**
40
62
* Check if the signature produced with "secret
41
63
* of size "secrent_len" (without the '\0') matches the original
42
64
* signature.
43
65
* Return true if it matches, false otherwise
44
66
*/
45
- bool check (const char * secret , size_t secret_len ) {
67
+ bool check (struct s_thread_data * data , const char * secret , size_t secret_len ) {
68
+ // If the secret was found by another thread, stop this thread
69
+ if (g_found_secret != NULL ) {
70
+ destroy_thread_data (data );
71
+ pthread_exit (NULL );
72
+ }
73
+
46
74
// Hash to_encrypt using HMAC into result
47
75
HMAC (
48
- g_evp_md ,
76
+ data -> g_evp_md ,
49
77
(const unsigned char * ) secret , secret_len ,
50
78
(const unsigned char * ) g_to_encrypt , g_to_encrypt_len ,
51
- g_result , & g_result_len
79
+ data -> g_result , & ( data -> g_result_len )
52
80
);
53
81
54
82
// Compare the computed hash to the given decoded base64 signature.
55
83
// If there is a match, we just found the key.
56
- return memcmp (g_result , g_signature , g_signature_len ) == 0 ;
84
+ return memcmp (data -> g_result , g_signature , g_signature_len ) == 0 ;
57
85
}
58
86
59
- bool bruteImpl ( char * str , int index , int maxDepth )
87
+ bool brute_impl ( struct s_thread_data * data , char * str , int index , int max_depth )
60
88
{
61
89
for (int i = 0 ; i < g_alphabet_len ; ++ i )
62
90
{
91
+ // The character at "index" in "str" successvely takes the value
92
+ // of each symbol in the alphabet
63
93
str [index ] = g_alphabet [i ];
64
94
65
- if (index == maxDepth - 1 ) {
66
- if (check ((const char * ) str , maxDepth )) return true;
67
- }
95
+ // If just changed the last letter, that means we generated a
96
+ // permutation, so we check it
97
+ if (index == max_depth - 1 ) {
98
+ // If we found the key, we return, otherwise we continue.
99
+ // By continuing, the current letter (at index "index")
100
+ // will be changed to the next symbol in the alphabet
101
+ if (check (data , (const char * ) str , max_depth )) return true;
102
+ }
103
+ // If the letter we just changed was not the last letter of
104
+ // the permutation we are generating, recurse to change the
105
+ // letter at the next index.
68
106
else {
69
- if (bruteImpl (str , index + 1 , maxDepth )) return true;
70
- }
107
+ // If this condition is met, that means we found the key.
108
+ // Otherwise the loop will continue and change the current
109
+ // character to the next letter in the alphabet.
110
+ if (brute_impl (data , str , index + 1 , max_depth )) return true;
111
+ }
71
112
}
72
113
114
+ // If we are here, we tried all the permutations without finding a match
73
115
return false;
74
116
}
75
117
76
- char * bruteSequential (int start , int maxLen )
118
+ /**
119
+ * Try all the combinations of secret starting with letter "starting_letter"
120
+ * and stopping at a maximum length of "max_len"
121
+ * Returns the key when there is a match, otherwise returns NULL
122
+ */
123
+ char * brute_sequential (struct s_thread_data * data )
77
124
{
78
- for (int i = start ; i <= maxLen ; ++ i )
79
- {
80
- if (bruteImpl (g_buffer , 0 , i ))
81
- return strdup (g_buffer );
125
+ // We set the starting letter
126
+ data -> g_buffer [0 ] = data -> starting_letter ;
127
+ // Special case for len = 1, we check in this function
128
+ if (check (data , data -> g_buffer , 1 )) {
129
+ // If this thread found the solution, set the shared global variable
130
+ // so other threads stop, and stop the current thread. Congrats little
131
+ // thread!
132
+ g_found_secret = strndup (data -> g_buffer , 1 );
133
+ return g_found_secret ;
134
+ }
135
+
136
+ // We start from length 2 (we handled the special case of length 1
137
+ // above.
138
+ for (size_t i = 2 ; i <= data -> max_len ; ++ i ) {
139
+ if (brute_impl (data , data -> g_buffer , 1 , i )) {
140
+ // If this thread found the solution, set the shared global variable
141
+ // so other threads stop, and stop the current thread. Congrats little
142
+ // thread!
143
+ g_found_secret = strndup (data -> g_buffer , i );
144
+ return g_found_secret ;
145
+ }
82
146
}
83
147
84
- return NULL ;
148
+ success :
149
+
150
+ return NULL ;
85
151
}
86
152
87
153
void usage (const char * cmd ) {
@@ -131,29 +197,26 @@ int main(int argc, char **argv) {
131
197
// is returned by this function
132
198
g_signature_len = Base64decode ((char * ) g_signature , (const char * ) g_signature_b64 );
133
199
134
- // Allocate the buffer used to hold the calculated signature
135
- g_result = malloc (EVP_MAX_MD_SIZE );
136
- g_buffer = malloc (max_len + 1 );
137
200
138
- // The chosen hash function is SHA-256
139
- g_evp_md = ( EVP_MD * ) EVP_sha256 ( );
201
+ struct s_thread_data * pointers_data [ g_alphabet_len ];
202
+ pthread_t * tid = malloc ( g_alphabet_len * sizeof ( pthread_t ) );
140
203
141
- clock_t start = clock (), diff ;
142
- char * secret = bruteSequential (1 , max_len );
143
- diff = clock () - start ;
204
+ for (size_t i = 0 ; i < g_alphabet_len ; i ++ ) {
205
+ pointers_data [i ] = malloc (sizeof (struct s_thread_data ));
206
+ init_thread_data (pointers_data [i ], g_alphabet [i ], max_len );
207
+ pthread_create (& tid [i ], NULL , (void * (* )(void * )) brute_sequential , pointers_data [i ]);
208
+ }
144
209
210
+ for (size_t i = 0 ; i < g_alphabet_len ; i ++ )
211
+ pthread_join (tid [i ], NULL );
145
212
146
- if (secret == NULL )
213
+ if (g_found_secret == NULL )
147
214
printf ("No solution found :-(\n" );
148
215
else
149
- printf ("Secret is \"%s\"\n" , secret );
150
-
151
- free (g_result );
152
- free (g_buffer );
153
- free (secret );
216
+ printf ("Secret is \"%s\"\n" , g_found_secret );
154
217
155
- int msec = diff * 1000 / CLOCKS_PER_SEC ;
156
- printf ( "Time taken %d seconds %d milliseconds" , msec / 1000 , msec % 1000 );
218
+ free ( g_found_secret ) ;
219
+ free ( tid );
157
220
158
221
return 0 ;
159
222
}
0 commit comments